Merge pull request #244 from safedep/feat/cloud-report-sync-v2

feat: Add SafeDep Cloud Integration as Optional Commands
This commit is contained in:
Abhisek Datta 2024-10-11 21:03:05 +05:30 committed by GitHub
commit aa501a76c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 1665 additions and 4507 deletions

View File

@ -2,7 +2,7 @@ SHELL := /bin/bash
GITCOMMIT := $(shell git rev-parse HEAD)
VERSION := "$(shell git describe --tags --abbrev=0)-$(shell git rev-parse --short HEAD)"
all: clean setup vet
all: quick-vet
linter-install:
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.0
@ -18,12 +18,6 @@ dev-setup: linter-install oapi-codegen-install protoc-install
oapi-codegen:
oapi-codegen -package insightapi -generate types ./api/insights-v1.yml > ./gen/insightapi/insights.types.go
oapi-codegen -package insightapi -generate client ./api/insights-v1.yml > ./gen/insightapi/insights.client.go
oapi-codegen -package cpv1trials -generate types ./api/cp-v1-trials.yml > ./gen/cpv1trials/trials.types.go
oapi-codegen -package cpv1trials -generate client ./api/cp-v1-trials.yml > ./gen/cpv1trials/trials.client.go
oapi-codegen -package cpv1 -generate types ./api/cp-v1.yml > ./gen/cpv1/cp.types.go
oapi-codegen -package cpv1 -generate client ./api/cp-v1.yml > ./gen/cpv1/cp.client.go
oapi-codegen -package syncv1 -generate types ./api/sync-v1.yml > ./gen/syncv1/sync.types.go
oapi-codegen -package syncv1 -generate client ./api/sync-v1.yml > ./gen/syncv1/sync.client.go
protoc-codegen:
protoc -I ./api \
@ -76,10 +70,10 @@ setup:
GO_CFLAGS=-X main.commit=$(GITCOMMIT) -X main.version=$(VERSION)
GO_LDFLAGS=-ldflags "-w $(GO_CFLAGS)"
vet: oapi-codegen protoc-codegen
quick-vet:
go build ${GO_LDFLAGS}
quick-vet:
vet: oapi-codegen protoc-codegen
go build ${GO_LDFLAGS}
.PHONY: test

View File

@ -1,8 +1,9 @@
<h1 align="center">
<img alt="SafeDep Vet" src="docs/static/img/vet-logo.png" width="150" />
</h1>
<p align="center">
🙌 Refer to <b><a href="https://safedep.io/docs/">https://safedep.io/docs</a></b> for the documentation 📖
Created and maintained by <b><a href="https://safedep.io/">https://safedep.io</a></b> with contributions from the community 🚀
</p>
[![Go Report Card](https://goreportcard.com/badge/github.com/safedep/vet)](https://goreportcard.com/report/github.com/safedep/vet)
@ -16,13 +17,13 @@
[![vet banner](docs/static/img/vet/vet-banner.png)](https://safedep.io/docs)
## Automate Open Source Package Vetting in CI/CD
## Policy as Code for Open Source Software Supply Chain
`vet` is a tool for identifying risks in open source software supply chain. It
goes beyond just vulnerabilities and provides visibility on OSS package risks
due to it's license, popularity, security hygiene, and more. `vet` is designed
with the goal of enabling trusted OSS package consumption by integrating with
CI/CD and `policy as code` as guardrails.
with the goal of helping software development teams consume safe and trusted
OSS components through automated vetting in CI/CD.
* [🔥 vet in action](#-vet-in-action)
* [Getting Started](#getting-started)
@ -43,6 +44,7 @@ CI/CD and `policy as code` as guardrails.
* [📖 Documentation](#-documentation)
* [🎊 Community](#-community)
* [💻 Development](#-development)
* [Support](#support)
* [Star History](#star-history)
* [🔖 References](#-references)
@ -281,6 +283,12 @@ First of all, thank you so much for showing interest in `vet`, we appreciate it
Refer to [CONTRIBUTING.md](CONTRIBUTING.md)
## Support
[SafeDep](https://safedep.io) provides enterprise support for `vet`
deployments. Check out [SafeDep Cloud](https://safedep.io) for large scale
deployment and management of `vet` in your organization.
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=safedep/vet&type=Date)](https://star-history.com/#safedep/vet&Date)

View File

@ -1,123 +0,0 @@
openapi: 3.0.2
info:
title: SafeDep Control Plane API for Trials Registration
contact:
name: SafeDep API
url: 'https://safedep.io'
description: |
Trials API provide a way for obtaining an API Key for data plane service access
using an Email Address. Trials is different from Registrations as the later
allows full access to the control plane while Trials is meant to allow access
only to a time bounded (expirable) API key for quick evaluation of tools.
version: 0.0.1
servers:
- url: 'https://{apiHost}/{apiBase}'
variables:
apiHost:
default: api.safedep.io
apiBase:
default: control-plane/v1
tags:
- name: Control Plane
description: Control Plane API
paths:
/trials:
post:
description: |
Register a trial user to obtain an expirable API Key. The API key will
be generated and sent to the user over Email to ensure validity and access
to the email by the requester. System defined limits will be applied to
maximum number of trial API keys that can be generated for an email.
operationId: registerTrialUser
tags:
- Control Plane
requestBody:
description: Trial registration request
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TrialRequest'
responses:
'201':
description: Successfully created an API key request
content:
application/json:
schema:
$ref: '#/components/schemas/TrialResponse'
'403':
description: Access to the API is denied
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
'429':
description: Rate limit block
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
'500':
description: Failed due to internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
components:
schemas:
ApiError:
type: object
properties:
message:
type: string
description: A descriptive message about the error meant for developer consumption
type:
type: string
description: An optional service or domain specific error group
enum:
- invalid_request
- operation_failed
- internal_error
code:
type: string
description: An error code identifying the error
enum:
- api_guard_invalid_credentials
- api_guard_rate_limit_exceeded
- api_guard_unauthorized
- api_guard_error
- app_generic_error
- app_security_error
- app_insufficient_parameters
- app_feature_not_enabled
- app_package_version_not_found
params:
type: object
description: Optional error specific attributes
additionalProperties:
type: object
properties:
key:
type: string
value:
type: string
TrialRequest:
type: object
properties:
email:
type: string
format: email
required:
- email
TrialResponse:
type: object
properties:
id:
type: string
minLength: 6
maxLength: 512
description: The ID of the trial registration request created in the system
expires_at:
type: string
format: date-time
description: The expiry time of the API key

View File

@ -1,113 +0,0 @@
openapi: 3.0.2
info:
title: SafeDep Control Plane API
contact:
name: SafeDep API
url: 'https://safedep.io'
description: |
The SafeDep Control Plane API provides configuration and management plane
access to clients for the SafeDep platform
version: 0.0.1
servers:
- url: 'https://{apiHost}/{apiBase}'
variables:
apiHost:
default: api.safedep.io
apiBase:
default: control-plane/v1
tags:
- name: Control Plane
description: Control Plane API
paths:
/auths/me:
get:
description: |
Introspection API for getting configuration information associated
with the supplied API credentials
operationId: getApiCredentialIntrospection
tags:
- Control Plane
responses:
'200':
description: API credentials introspection information
content:
application/json:
schema:
$ref: '#/components/schemas/CredentialIntrospectionResponse'
'403':
description: Access to the API is denied
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
'429':
description: Rate limit block
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
'500':
description: Failed due to internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
components:
securitySchemes:
api_key:
type: apiKey
name: Authorization
in: header
schemas:
ApiError:
type: object
properties:
message:
type: string
description: A descriptive message about the error meant for developer consumption
type:
type: string
description: An optional service or domain specific error group
enum:
- invalid_request
- operation_failed
- internal_error
code:
type: string
description: An error code identifying the error
enum:
- api_guard_invalid_credentials
- api_guard_rate_limit_exceeded
- api_guard_unauthorized
- api_guard_error
- app_generic_error
- app_security_error
- app_insufficient_parameters
- app_feature_not_enabled
- app_package_version_not_found
params:
type: object
description: Optional error specific attributes
additionalProperties:
type: object
properties:
key:
type: string
value:
type: string
CredentialIntrospectionResponse:
type: object
properties:
type:
type: string
description: The entity type to which this credential belongs to
enum:
- TrialUser
- Organization
- Team
- User
expiry:
type: string
description: Expiry timestamp in RFC3399 format if expirable
required:
- type

File diff suppressed because it is too large Load Diff

96
auth.go
View File

@ -2,7 +2,6 @@ package main
import (
"errors"
"fmt"
"os"
"github.com/AlecAivazis/survey/v2"
@ -14,27 +13,20 @@ import (
)
var (
authInsightApiBaseUrl string
authControlPlaneApiBaseUrl string
authTrialEmail string
authCommunity bool
authTenantDomain string
)
func newAuthCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "auth",
Short: "Configure and verify Insights API authentication",
Short: "Configure vet authentication",
RunE: func(cmd *cobra.Command, args []string) error {
return errors.New("a valid sub-command is required")
},
}
cmd.PersistentFlags().StringVarP(&authControlPlaneApiBaseUrl, "control-plane", "",
auth.DefaultControlPlaneApiUrl(), "Base URL of Control Plane API")
cmd.AddCommand(configureAuthCommand())
cmd.AddCommand(verifyAuthCommand())
cmd.AddCommand(trialsRegisterCommand())
return cmd
}
@ -46,25 +38,40 @@ func configureAuthCommand() *cobra.Command {
var key string
var err error
if !authCommunity {
err = survey.AskOne(&survey.Password{
Message: "Enter the API key",
}, &key)
} else {
authInsightApiBaseUrl = auth.DefaultCommunityApiUrl()
}
err = survey.AskOne(&survey.Password{
Message: "Enter the API key",
}, &key)
if err != nil {
logger.Fatalf("Failed to setup auth: %v", err)
}
err = auth.Configure(auth.Config{
ApiUrl: authInsightApiBaseUrl,
ApiKey: string(key),
ControlPlaneApiUrl: authControlPlaneApiBaseUrl,
Community: authCommunity,
})
if auth.TenantDomain() != "" && auth.TenantDomain() != authTenantDomain {
ui.PrintWarning("Tenant domain mismatch. Existing: %s, New: %s, continue? ",
auth.TenantDomain(), authTenantDomain)
var confirm bool
err = survey.AskOne(&survey.Confirm{
Message: "Do you want to continue?",
}, &confirm)
if err != nil {
logger.Fatalf("Failed to setup auth: %v", err)
}
if !confirm {
return nil
}
}
auth.SetRuntimeCloudTenant(authTenantDomain)
auth.SetRuntimeApiKey(key)
err = auth.Verify()
if err != nil {
logger.Fatalf("Failed to verify auth: %v", err)
}
err = auth.PersistApiKey(key, authTenantDomain)
if err != nil {
logger.Fatalf("Failed to configure auth: %v", err)
}
@ -74,10 +81,10 @@ func configureAuthCommand() *cobra.Command {
},
}
cmd.Flags().StringVarP(&authInsightApiBaseUrl, "api", "", auth.DefaultApiUrl(),
"Base URL of Insights API")
cmd.Flags().BoolVarP(&authCommunity, "community", "", false,
"Use community API endpoint for Insights")
cmd.Flags().StringVarP(&authTenantDomain, "tenant", "", "",
"Tenant domain for SafeDep Cloud")
_ = cmd.MarkFlagRequired("tenant")
return cmd
}
@ -90,9 +97,7 @@ func verifyAuthCommand() *cobra.Command {
ui.PrintSuccess("Running in Community Mode")
}
failOnError("auth/verify", auth.Verify(&auth.VerifyConfig{
ControlPlaneApiUrl: authControlPlaneApiBaseUrl,
}))
failOnError("auth/verify", auth.Verify())
ui.PrintSuccess("Authentication key is valid!")
return nil
@ -101,32 +106,3 @@ func verifyAuthCommand() *cobra.Command {
return cmd
}
func trialsRegisterCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "trial",
RunE: func(cmd *cobra.Command, args []string) error {
client := auth.NewTrialRegistrationClient(auth.TrialConfig{
Email: authTrialEmail,
ControlPlaneApiUrl: authControlPlaneApiBaseUrl,
})
res, err := client.Execute()
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Trial registration successful with Id:%s\n", res.Id)
fmt.Printf("Check your email (%s) for API key and usage instructions\n", authTrialEmail)
fmt.Printf("The trial API key will expire on %s\n", res.ExpiresAt.String())
return nil
},
}
cmd.Flags().StringVarP(&authTrialEmail, "email", "", "",
"Email address to use for sending trial API key")
return cmd
}

83
cmd/cloud/key.go Normal file
View File

@ -0,0 +1,83 @@
package cloud
import (
"time"
"github.com/safedep/vet/internal/auth"
"github.com/safedep/vet/internal/ui"
"github.com/safedep/vet/pkg/cloud"
"github.com/safedep/vet/pkg/common/logger"
"github.com/spf13/cobra"
)
var (
keyName string
keyDescription string
keyExpiresIn int
)
func newKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "key",
Short: "Manage API keys",
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.Help()
},
}
cmd.AddCommand(newKeyCreateCommand())
return cmd
}
func newKeyCreateCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Short: "Create a new API key",
RunE: func(cmd *cobra.Command, args []string) error {
err := executeCreateKey()
if err != nil {
logger.Errorf("Failed to create API key: %v", err)
}
return nil
},
}
cmd.Flags().StringVar(&keyName, "name", "", "Name of the API key")
cmd.Flags().StringVar(&keyDescription, "description", "", "Description of the API key")
cmd.Flags().IntVar(&keyExpiresIn, "expires-in", 30,
"Number of days after which the API key will expire")
_ = cmd.MarkFlagRequired("name")
return cmd
}
func executeCreateKey() error {
client, err := auth.ControlPlaneClientConnection("vet-cloud-key-create")
if err != nil {
return err
}
keyService, err := cloud.NewApiKeyService(client)
if err != nil {
return err
}
key, err := keyService.CreateApiKey(&cloud.CreateApiKeyRequest{
Name: keyName,
Desc: keyDescription,
ExpiryInDays: keyExpiresIn,
})
if err != nil {
return err
}
ui.PrintSuccess("API key created successfully.")
ui.PrintSuccess("Key: %s", key.Key)
ui.PrintSuccess("Expires at: %s", key.ExpiresAt.Format(time.RFC3339))
return nil
}

57
cmd/cloud/login.go Normal file
View File

@ -0,0 +1,57 @@
package cloud
import (
"context"
"fmt"
"net/http"
"github.com/cli/oauth/device"
"github.com/safedep/vet/internal/auth"
"github.com/safedep/vet/internal/ui"
"github.com/safedep/vet/pkg/common/logger"
"github.com/spf13/cobra"
)
func newCloudLoginCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "login",
Short: "Login to SafeDep cloud for management tasks",
RunE: func(cmd *cobra.Command, args []string) error {
err := executeDeviceAuthFlow()
if err != nil {
logger.Errorf("Failed to login to the SafeDep cloud: %v", err)
}
return nil
},
}
return cmd
}
func executeDeviceAuthFlow() error {
code, err := device.RequestCode(http.DefaultClient,
auth.CloudIdentityServiceDeviceCodeUrl(),
auth.CloudIdentityServiceClientId(),
[]string{"offline_access", "openid", "profile", "email"},
device.WithAudience(auth.CloudIdentityServiceAudience()))
if err != nil {
return fmt.Errorf("failed to request device code: %w", err)
}
ui.PrintSuccess("Please visit %s and enter the code %s to authenticate",
code.VerificationURIComplete, code.UserCode)
token, err := device.Wait(context.TODO(),
http.DefaultClient, auth.CloudIdentityServiceTokenUrl(),
device.WaitOptions{
ClientID: auth.CloudIdentityServiceClientId(),
DeviceCode: code,
})
if err != nil {
return fmt.Errorf("failed to authenticate: %w", err)
}
return auth.PersistCloudTokens(token.Token,
token.RefreshToken, tenantDomain)
}

36
cmd/cloud/main.go Normal file
View File

@ -0,0 +1,36 @@
package cloud
import (
"github.com/safedep/vet/internal/auth"
"github.com/spf13/cobra"
)
var tenantDomain string
func NewCloudCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "cloud",
Short: "Manage and query cloud resources (control plane)",
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.Help()
},
}
cmd.PersistentFlags().StringVar(&tenantDomain, "tenant", "",
"Tenant domain to use for the command")
cmd.AddCommand(newCloudLoginCommand())
cmd.AddCommand(newRegisterCommand())
cmd.AddCommand(newQueryCommand())
cmd.AddCommand(newPingCommand())
cmd.AddCommand(newWhoamiCommand())
cmd.AddCommand(newKeyCommand())
cmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
if tenantDomain != "" {
auth.SetRuntimeCloudTenant(tenantDomain)
}
}
return cmd
}

49
cmd/cloud/ping.go Normal file
View File

@ -0,0 +1,49 @@
package cloud
import (
"time"
"github.com/safedep/vet/internal/auth"
"github.com/safedep/vet/internal/ui"
"github.com/safedep/vet/pkg/cloud"
"github.com/safedep/vet/pkg/common/logger"
"github.com/spf13/cobra"
)
func newPingCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "ping",
Short: "Ping the control plane to check authentication and connectivity",
RunE: func(cmd *cobra.Command, args []string) error {
err := pingControlPlane()
if err != nil {
logger.Errorf("Failed to ping control plane: %v", err)
}
return nil
},
}
return cmd
}
func pingControlPlane() error {
conn, err := auth.ControlPlaneClientConnection("vet-cloud-ping")
if err != nil {
return err
}
pingService, err := cloud.NewPingService(conn)
if err != nil {
return err
}
pr, err := pingService.Ping()
if err != nil {
return err
}
ui.PrintSuccess("Ping successful. Started at %s, finished at %s",
pr.StartedAt.Format(time.RFC3339), pr.FinishedAt.Format(time.RFC3339))
return nil
}

116
cmd/cloud/query.go Normal file
View File

@ -0,0 +1,116 @@
package cloud
import (
"errors"
"os"
"sort"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/safedep/vet/internal/auth"
"github.com/safedep/vet/internal/ui"
"github.com/safedep/vet/pkg/cloud/query"
"github.com/safedep/vet/pkg/common/logger"
"github.com/spf13/cobra"
)
var (
querySql string
)
func newQueryCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "query",
Short: "Query risks by executing SQL queries",
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.Help()
},
}
cmd.AddCommand(newQueryExecuteCommand())
return cmd
}
func newQueryExecuteCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "execute",
Short: "Execute a query",
RunE: func(cmd *cobra.Command, args []string) error {
err := executeQuery()
if err != nil {
logger.Errorf("Failed to execute query: %v", err)
}
return nil
},
}
cmd.Flags().StringVarP(&querySql, "sql", "s", "", "SQL query to execute")
return cmd
}
func executeQuery() error {
if querySql == "" {
return errors.New("SQL string is required")
}
client, err := auth.ControlPlaneClientConnection("vet-cloud-query")
if err != nil {
return err
}
queryService, err := query.NewQueryService(client)
if err != nil {
return err
}
response, err := queryService.ExecuteSql(querySql)
if err != nil {
return err
}
return renderQueryResponseAsTable(response)
}
func renderQueryResponseAsTable(response *query.QueryResponse) error {
tbl := table.NewWriter()
tbl.SetOutputMirror(os.Stdout)
tbl.SetStyle(table.StyleLight)
if response.Count() == 0 {
logger.Infof("No results found")
return nil
}
ui.PrintSuccess("Query returned %d results", response.Count())
// Header
headers := []string{}
response.GetRow(0).ForEachField(func(key string, _ interface{}) {
headers = append(headers, key)
})
sort.Strings(headers)
headerRow := []interface{}{}
for _, header := range headers {
headerRow = append(headerRow, header)
}
tbl.AppendHeader(headerRow)
// Ensure we have a consistent order of columns
response.ForEachRow(func(row *query.QueryRow) {
rowValues := []interface{}{}
for _, header := range headers {
rowValues = append(rowValues, row.GetField(header))
}
tbl.AppendRow(rowValues)
tbl.AppendSeparator()
})
tbl.Render()
return nil
}

77
cmd/cloud/register.go Normal file
View File

@ -0,0 +1,77 @@
package cloud
import (
"fmt"
"github.com/safedep/vet/internal/auth"
"github.com/safedep/vet/internal/ui"
"github.com/safedep/vet/pkg/cloud"
"github.com/safedep/vet/pkg/common/logger"
"github.com/spf13/cobra"
)
var (
registerEmail string
registerName string
registerOrgName string
registerOrgDomain string
)
func newRegisterCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "register",
Short: "Register a new user and tenant",
RunE: func(cmd *cobra.Command, args []string) error {
err := registerUserTenant()
if err != nil {
logger.Errorf("Failed to register user: %v", err)
}
return nil
},
}
cmd.Flags().StringVar(&registerEmail, "email", "cloud@safedep.io", "Email of the user (not required for SafeDep cloud)")
cmd.Flags().StringVar(&registerName, "name", "", "Name of the user")
cmd.Flags().StringVar(&registerOrgName, "org-name", "", "Name of the organization")
cmd.Flags().StringVar(&registerOrgDomain, "org-domain", "", "Domain of the organization")
_ = cmd.MarkFlagRequired("name")
_ = cmd.MarkFlagRequired("org-name")
_ = cmd.MarkFlagRequired("org-domain")
return cmd
}
func registerUserTenant() error {
conn, err := auth.ControlPlaneClientConnection("vet-cloud-register")
if err != nil {
return err
}
onboardingService, err := cloud.NewOnboardingService(conn)
if err != nil {
return err
}
res, err := onboardingService.Register(&cloud.RegisterRequest{
Name: registerName,
Email: registerEmail,
OrgName: registerOrgName,
OrgDomain: registerOrgDomain,
})
if err != nil {
return err
}
ui.PrintSuccess("Registered user and tenant.")
ui.PrintSuccess("Tenant domain: %s", res.TenantDomain)
err = auth.PersistTenantDomain(res.TenantDomain)
if err != nil {
return fmt.Errorf("failed to persist tenant domain: %w", err)
}
return nil
}

54
cmd/cloud/whoami.go Normal file
View File

@ -0,0 +1,54 @@
package cloud
import (
"github.com/safedep/vet/internal/auth"
"github.com/safedep/vet/internal/ui"
"github.com/safedep/vet/pkg/cloud"
"github.com/safedep/vet/pkg/common/logger"
"github.com/spf13/cobra"
)
func newWhoamiCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "whoami",
Short: "Print information about the current user",
RunE: func(cmd *cobra.Command, args []string) error {
err := executeWhoami()
if err != nil {
logger.Errorf("Failed to execute whoami: %v", err)
}
return nil
},
}
return cmd
}
func executeWhoami() error {
conn, err := auth.ControlPlaneClientConnection("vet-cloud-whoami")
if err != nil {
return err
}
userService, err := cloud.NewUserService(conn)
if err != nil {
return err
}
res, err := userService.CurrentUserInfo()
if err != nil {
return err
}
ui.PrintSuccess("Authenticated as: %s <%s>", res.GetUser().GetName(),
res.GetUser().GetEmail())
ui.PrintSuccess("Has access to the following tenants:")
for _, access := range res.GetAccess() {
ui.PrintSuccess(" - %s [%d]", access.GetTenant().GetDomain(),
access.GetLevel())
}
return nil
}

View File

@ -1,297 +0,0 @@
// Package cpv1trials provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/deepmap/oapi-codegen version v1.10.1 DO NOT EDIT.
package cpv1trials
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
)
// RequestEditorFn is the function signature for the RequestEditor callback function
type RequestEditorFn func(ctx context.Context, req *http.Request) error
// Doer performs HTTP requests.
//
// The standard http.Client implements this interface.
type HttpRequestDoer interface {
Do(req *http.Request) (*http.Response, error)
}
// Client which conforms to the OpenAPI3 specification for this service.
type Client struct {
// The endpoint of the server conforming to this interface, with scheme,
// https://api.deepmap.com for example. This can contain a path relative
// to the server, such as https://api.deepmap.com/dev-test, and all the
// paths in the swagger spec will be appended to the server.
Server string
// Doer for performing requests, typically a *http.Client with any
// customized settings, such as certificate chains.
Client HttpRequestDoer
// A list of callbacks for modifying requests which are generated before sending over
// the network.
RequestEditors []RequestEditorFn
}
// ClientOption allows setting custom parameters during construction
type ClientOption func(*Client) error
// Creates a new Client, with reasonable defaults
func NewClient(server string, opts ...ClientOption) (*Client, error) {
// create a client with sane default values
client := Client{
Server: server,
}
// mutate client and add all optional params
for _, o := range opts {
if err := o(&client); err != nil {
return nil, err
}
}
// ensure the server URL always has a trailing slash
if !strings.HasSuffix(client.Server, "/") {
client.Server += "/"
}
// create httpClient, if not already present
if client.Client == nil {
client.Client = &http.Client{}
}
return &client, nil
}
// WithHTTPClient allows overriding the default Doer, which is
// automatically created using http.Client. This is useful for tests.
func WithHTTPClient(doer HttpRequestDoer) ClientOption {
return func(c *Client) error {
c.Client = doer
return nil
}
}
// WithRequestEditorFn allows setting up a callback function, which will be
// called right before sending the request. This can be used to mutate the request.
func WithRequestEditorFn(fn RequestEditorFn) ClientOption {
return func(c *Client) error {
c.RequestEditors = append(c.RequestEditors, fn)
return nil
}
}
// The interface specification for the client above.
type ClientInterface interface {
// RegisterTrialUser request with any body
RegisterTrialUserWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
RegisterTrialUser(ctx context.Context, body RegisterTrialUserJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
}
func (c *Client) RegisterTrialUserWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewRegisterTrialUserRequestWithBody(c.Server, contentType, body)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
func (c *Client) RegisterTrialUser(ctx context.Context, body RegisterTrialUserJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewRegisterTrialUserRequest(c.Server, body)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
// NewRegisterTrialUserRequest calls the generic RegisterTrialUser builder with application/json body
func NewRegisterTrialUserRequest(server string, body RegisterTrialUserJSONRequestBody) (*http.Request, error) {
var bodyReader io.Reader
buf, err := json.Marshal(body)
if err != nil {
return nil, err
}
bodyReader = bytes.NewReader(buf)
return NewRegisterTrialUserRequestWithBody(server, "application/json", bodyReader)
}
// NewRegisterTrialUserRequestWithBody generates requests for RegisterTrialUser with any type of body
func NewRegisterTrialUserRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) {
var err error
serverURL, err := url.Parse(server)
if err != nil {
return nil, err
}
operationPath := fmt.Sprintf("/trials")
if operationPath[0] == '/' {
operationPath = "." + operationPath
}
queryURL, err := serverURL.Parse(operationPath)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", queryURL.String(), body)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", contentType)
return req, nil
}
func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error {
for _, r := range c.RequestEditors {
if err := r(ctx, req); err != nil {
return err
}
}
for _, r := range additionalEditors {
if err := r(ctx, req); err != nil {
return err
}
}
return nil
}
// ClientWithResponses builds on ClientInterface to offer response payloads
type ClientWithResponses struct {
ClientInterface
}
// NewClientWithResponses creates a new ClientWithResponses, which wraps
// Client with return type handling
func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) {
client, err := NewClient(server, opts...)
if err != nil {
return nil, err
}
return &ClientWithResponses{client}, nil
}
// WithBaseURL overrides the baseURL.
func WithBaseURL(baseURL string) ClientOption {
return func(c *Client) error {
newBaseURL, err := url.Parse(baseURL)
if err != nil {
return err
}
c.Server = newBaseURL.String()
return nil
}
}
// ClientWithResponsesInterface is the interface specification for the client with responses above.
type ClientWithResponsesInterface interface {
// RegisterTrialUser request with any body
RegisterTrialUserWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RegisterTrialUserResponse, error)
RegisterTrialUserWithResponse(ctx context.Context, body RegisterTrialUserJSONRequestBody, reqEditors ...RequestEditorFn) (*RegisterTrialUserResponse, error)
}
type RegisterTrialUserResponse struct {
Body []byte
HTTPResponse *http.Response
JSON201 *TrialResponse
JSON403 *ApiError
JSON429 *ApiError
JSON500 *ApiError
}
// Status returns HTTPResponse.Status
func (r RegisterTrialUserResponse) Status() string {
if r.HTTPResponse != nil {
return r.HTTPResponse.Status
}
return http.StatusText(0)
}
// StatusCode returns HTTPResponse.StatusCode
func (r RegisterTrialUserResponse) StatusCode() int {
if r.HTTPResponse != nil {
return r.HTTPResponse.StatusCode
}
return 0
}
// RegisterTrialUserWithBodyWithResponse request with arbitrary body returning *RegisterTrialUserResponse
func (c *ClientWithResponses) RegisterTrialUserWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RegisterTrialUserResponse, error) {
rsp, err := c.RegisterTrialUserWithBody(ctx, contentType, body, reqEditors...)
if err != nil {
return nil, err
}
return ParseRegisterTrialUserResponse(rsp)
}
func (c *ClientWithResponses) RegisterTrialUserWithResponse(ctx context.Context, body RegisterTrialUserJSONRequestBody, reqEditors ...RequestEditorFn) (*RegisterTrialUserResponse, error) {
rsp, err := c.RegisterTrialUser(ctx, body, reqEditors...)
if err != nil {
return nil, err
}
return ParseRegisterTrialUserResponse(rsp)
}
// ParseRegisterTrialUserResponse parses an HTTP response from a RegisterTrialUserWithResponse call
func ParseRegisterTrialUserResponse(rsp *http.Response) (*RegisterTrialUserResponse, error) {
bodyBytes, err := ioutil.ReadAll(rsp.Body)
defer func() { _ = rsp.Body.Close() }()
if err != nil {
return nil, err
}
response := &RegisterTrialUserResponse{
Body: bodyBytes,
HTTPResponse: rsp,
}
switch {
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201:
var dest TrialResponse
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON201 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403:
var dest ApiError
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON403 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429:
var dest ApiError
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON429 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500:
var dest ApiError
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON500 = &dest
}
return response, nil
}

View File

@ -1,159 +0,0 @@
// Package cpv1trials provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/deepmap/oapi-codegen version v1.10.1 DO NOT EDIT.
package cpv1trials
import (
"encoding/json"
"fmt"
"time"
openapi_types "github.com/deepmap/oapi-codegen/pkg/types"
)
// Defines values for ApiErrorCode.
const (
ApiErrorCodeApiGuardError ApiErrorCode = "api_guard_error"
ApiErrorCodeApiGuardInvalidCredentials ApiErrorCode = "api_guard_invalid_credentials"
ApiErrorCodeApiGuardRateLimitExceeded ApiErrorCode = "api_guard_rate_limit_exceeded"
ApiErrorCodeApiGuardUnauthorized ApiErrorCode = "api_guard_unauthorized"
ApiErrorCodeAppFeatureNotEnabled ApiErrorCode = "app_feature_not_enabled"
ApiErrorCodeAppGenericError ApiErrorCode = "app_generic_error"
ApiErrorCodeAppInsufficientParameters ApiErrorCode = "app_insufficient_parameters"
ApiErrorCodeAppPackageVersionNotFound ApiErrorCode = "app_package_version_not_found"
ApiErrorCodeAppSecurityError ApiErrorCode = "app_security_error"
)
// Defines values for ApiErrorType.
const (
ApiErrorTypeInternalError ApiErrorType = "internal_error"
ApiErrorTypeInvalidRequest ApiErrorType = "invalid_request"
ApiErrorTypeOperationFailed ApiErrorType = "operation_failed"
)
// ApiError defines model for ApiError.
type ApiError struct {
// An error code identifying the error
Code *ApiErrorCode `json:"code,omitempty"`
// A descriptive message about the error meant for developer consumption
Message *string `json:"message,omitempty"`
// Optional error specific attributes
Params *ApiError_Params `json:"params,omitempty"`
// An optional service or domain specific error group
Type *ApiErrorType `json:"type,omitempty"`
}
// An error code identifying the error
type ApiErrorCode string
// Optional error specific attributes
type ApiError_Params struct {
AdditionalProperties map[string]struct {
Key *string `json:"key,omitempty"`
Value *string `json:"value,omitempty"`
} `json:"-"`
}
// An optional service or domain specific error group
type ApiErrorType string
// TrialRequest defines model for TrialRequest.
type TrialRequest struct {
Email openapi_types.Email `json:"email"`
}
// TrialResponse defines model for TrialResponse.
type TrialResponse struct {
// The expiry time of the API key
ExpiresAt *time.Time `json:"expires_at,omitempty"`
// The ID of the trial registration request created in the system
Id *string `json:"id,omitempty"`
}
// RegisterTrialUserJSONBody defines parameters for RegisterTrialUser.
type RegisterTrialUserJSONBody TrialRequest
// RegisterTrialUserJSONRequestBody defines body for RegisterTrialUser for application/json ContentType.
type RegisterTrialUserJSONRequestBody RegisterTrialUserJSONBody
// Getter for additional properties for ApiError_Params. Returns the specified
// element and whether it was found
func (a ApiError_Params) Get(fieldName string) (value struct {
Key *string `json:"key,omitempty"`
Value *string `json:"value,omitempty"`
}, found bool) {
if a.AdditionalProperties != nil {
value, found = a.AdditionalProperties[fieldName]
}
return
}
// Setter for additional properties for ApiError_Params
func (a *ApiError_Params) Set(fieldName string, value struct {
Key *string `json:"key,omitempty"`
Value *string `json:"value,omitempty"`
}) {
if a.AdditionalProperties == nil {
a.AdditionalProperties = make(map[string]struct {
Key *string `json:"key,omitempty"`
Value *string `json:"value,omitempty"`
})
}
a.AdditionalProperties[fieldName] = value
}
// Override default JSON handling for ApiError_Params to handle AdditionalProperties
func (a *ApiError_Params) UnmarshalJSON(b []byte) error {
object := make(map[string]json.RawMessage)
err := json.Unmarshal(b, &object)
if err != nil {
return err
}
if len(object) != 0 {
a.AdditionalProperties = make(map[string]struct {
Key *string `json:"key,omitempty"`
Value *string `json:"value,omitempty"`
})
for fieldName, fieldBuf := range object {
var fieldVal struct {
Key *string `json:"key,omitempty"`
Value *string `json:"value,omitempty"`
}
err := json.Unmarshal(fieldBuf, &fieldVal)
if err != nil {
return fmt.Errorf("error unmarshaling field %s: %w", fieldName, err)
}
a.AdditionalProperties[fieldName] = fieldVal
}
}
return nil
}
// Override default JSON handling for ApiError_Params to handle AdditionalProperties
func (a ApiError_Params) MarshalJSON() ([]byte, error) {
var err error
object := make(map[string]json.RawMessage)
for fieldName, field := range a.AdditionalProperties {
object[fieldName], err = json.Marshal(field)
if err != nil {
return nil, fmt.Errorf("error marshaling '%s': %w", fieldName, err)
}
}
return json.Marshal(object)
}

View File

@ -1,639 +0,0 @@
// Package syncv1 provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/deepmap/oapi-codegen version v1.10.1 DO NOT EDIT.
package syncv1
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
"github.com/deepmap/oapi-codegen/pkg/runtime"
)
// RequestEditorFn is the function signature for the RequestEditor callback function
type RequestEditorFn func(ctx context.Context, req *http.Request) error
// Doer performs HTTP requests.
//
// The standard http.Client implements this interface.
type HttpRequestDoer interface {
Do(req *http.Request) (*http.Response, error)
}
// Client which conforms to the OpenAPI3 specification for this service.
type Client struct {
// The endpoint of the server conforming to this interface, with scheme,
// https://api.deepmap.com for example. This can contain a path relative
// to the server, such as https://api.deepmap.com/dev-test, and all the
// paths in the swagger spec will be appended to the server.
Server string
// Doer for performing requests, typically a *http.Client with any
// customized settings, such as certificate chains.
Client HttpRequestDoer
// A list of callbacks for modifying requests which are generated before sending over
// the network.
RequestEditors []RequestEditorFn
}
// ClientOption allows setting custom parameters during construction
type ClientOption func(*Client) error
// Creates a new Client, with reasonable defaults
func NewClient(server string, opts ...ClientOption) (*Client, error) {
// create a client with sane default values
client := Client{
Server: server,
}
// mutate client and add all optional params
for _, o := range opts {
if err := o(&client); err != nil {
return nil, err
}
}
// ensure the server URL always has a trailing slash
if !strings.HasSuffix(client.Server, "/") {
client.Server += "/"
}
// create httpClient, if not already present
if client.Client == nil {
client.Client = &http.Client{}
}
return &client, nil
}
// WithHTTPClient allows overriding the default Doer, which is
// automatically created using http.Client. This is useful for tests.
func WithHTTPClient(doer HttpRequestDoer) ClientOption {
return func(c *Client) error {
c.Client = doer
return nil
}
}
// WithRequestEditorFn allows setting up a callback function, which will be
// called right before sending the request. This can be used to mutate the request.
func WithRequestEditorFn(fn RequestEditorFn) ClientOption {
return func(c *Client) error {
c.RequestEditors = append(c.RequestEditors, fn)
return nil
}
}
// The interface specification for the client above.
type ClientInterface interface {
// CreateSyncJob request with any body
CreateSyncJobWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
CreateSyncJob(ctx context.Context, body CreateSyncJobJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
// UpdateSyncJob request with any body
UpdateSyncJobWithBody(ctx context.Context, jobId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
UpdateSyncJob(ctx context.Context, jobId string, body UpdateSyncJobJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
// CreateJobIssue request with any body
CreateJobIssueWithBody(ctx context.Context, jobId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
CreateJobIssue(ctx context.Context, jobId string, body CreateJobIssueJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
}
func (c *Client) CreateSyncJobWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewCreateSyncJobRequestWithBody(c.Server, contentType, body)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
func (c *Client) CreateSyncJob(ctx context.Context, body CreateSyncJobJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewCreateSyncJobRequest(c.Server, body)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
func (c *Client) UpdateSyncJobWithBody(ctx context.Context, jobId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewUpdateSyncJobRequestWithBody(c.Server, jobId, contentType, body)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
func (c *Client) UpdateSyncJob(ctx context.Context, jobId string, body UpdateSyncJobJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewUpdateSyncJobRequest(c.Server, jobId, body)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
func (c *Client) CreateJobIssueWithBody(ctx context.Context, jobId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewCreateJobIssueRequestWithBody(c.Server, jobId, contentType, body)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
func (c *Client) CreateJobIssue(ctx context.Context, jobId string, body CreateJobIssueJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewCreateJobIssueRequest(c.Server, jobId, body)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
// NewCreateSyncJobRequest calls the generic CreateSyncJob builder with application/json body
func NewCreateSyncJobRequest(server string, body CreateSyncJobJSONRequestBody) (*http.Request, error) {
var bodyReader io.Reader
buf, err := json.Marshal(body)
if err != nil {
return nil, err
}
bodyReader = bytes.NewReader(buf)
return NewCreateSyncJobRequestWithBody(server, "application/json", bodyReader)
}
// NewCreateSyncJobRequestWithBody generates requests for CreateSyncJob with any type of body
func NewCreateSyncJobRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) {
var err error
serverURL, err := url.Parse(server)
if err != nil {
return nil, err
}
operationPath := fmt.Sprintf("/jobs")
if operationPath[0] == '/' {
operationPath = "." + operationPath
}
queryURL, err := serverURL.Parse(operationPath)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", queryURL.String(), body)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", contentType)
return req, nil
}
// NewUpdateSyncJobRequest calls the generic UpdateSyncJob builder with application/json body
func NewUpdateSyncJobRequest(server string, jobId string, body UpdateSyncJobJSONRequestBody) (*http.Request, error) {
var bodyReader io.Reader
buf, err := json.Marshal(body)
if err != nil {
return nil, err
}
bodyReader = bytes.NewReader(buf)
return NewUpdateSyncJobRequestWithBody(server, jobId, "application/json", bodyReader)
}
// NewUpdateSyncJobRequestWithBody generates requests for UpdateSyncJob with any type of body
func NewUpdateSyncJobRequestWithBody(server string, jobId string, contentType string, body io.Reader) (*http.Request, error) {
var err error
var pathParam0 string
pathParam0, err = runtime.StyleParamWithLocation("simple", false, "job_id", runtime.ParamLocationPath, jobId)
if err != nil {
return nil, err
}
serverURL, err := url.Parse(server)
if err != nil {
return nil, err
}
operationPath := fmt.Sprintf("/jobs/%s", pathParam0)
if operationPath[0] == '/' {
operationPath = "." + operationPath
}
queryURL, err := serverURL.Parse(operationPath)
if err != nil {
return nil, err
}
req, err := http.NewRequest("PUT", queryURL.String(), body)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", contentType)
return req, nil
}
// NewCreateJobIssueRequest calls the generic CreateJobIssue builder with application/json body
func NewCreateJobIssueRequest(server string, jobId string, body CreateJobIssueJSONRequestBody) (*http.Request, error) {
var bodyReader io.Reader
buf, err := json.Marshal(body)
if err != nil {
return nil, err
}
bodyReader = bytes.NewReader(buf)
return NewCreateJobIssueRequestWithBody(server, jobId, "application/json", bodyReader)
}
// NewCreateJobIssueRequestWithBody generates requests for CreateJobIssue with any type of body
func NewCreateJobIssueRequestWithBody(server string, jobId string, contentType string, body io.Reader) (*http.Request, error) {
var err error
var pathParam0 string
pathParam0, err = runtime.StyleParamWithLocation("simple", false, "job_id", runtime.ParamLocationPath, jobId)
if err != nil {
return nil, err
}
serverURL, err := url.Parse(server)
if err != nil {
return nil, err
}
operationPath := fmt.Sprintf("/jobs/%s/issues", pathParam0)
if operationPath[0] == '/' {
operationPath = "." + operationPath
}
queryURL, err := serverURL.Parse(operationPath)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", queryURL.String(), body)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", contentType)
return req, nil
}
func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error {
for _, r := range c.RequestEditors {
if err := r(ctx, req); err != nil {
return err
}
}
for _, r := range additionalEditors {
if err := r(ctx, req); err != nil {
return err
}
}
return nil
}
// ClientWithResponses builds on ClientInterface to offer response payloads
type ClientWithResponses struct {
ClientInterface
}
// NewClientWithResponses creates a new ClientWithResponses, which wraps
// Client with return type handling
func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) {
client, err := NewClient(server, opts...)
if err != nil {
return nil, err
}
return &ClientWithResponses{client}, nil
}
// WithBaseURL overrides the baseURL.
func WithBaseURL(baseURL string) ClientOption {
return func(c *Client) error {
newBaseURL, err := url.Parse(baseURL)
if err != nil {
return err
}
c.Server = newBaseURL.String()
return nil
}
}
// ClientWithResponsesInterface is the interface specification for the client with responses above.
type ClientWithResponsesInterface interface {
// CreateSyncJob request with any body
CreateSyncJobWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateSyncJobResponse, error)
CreateSyncJobWithResponse(ctx context.Context, body CreateSyncJobJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateSyncJobResponse, error)
// UpdateSyncJob request with any body
UpdateSyncJobWithBodyWithResponse(ctx context.Context, jobId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateSyncJobResponse, error)
UpdateSyncJobWithResponse(ctx context.Context, jobId string, body UpdateSyncJobJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateSyncJobResponse, error)
// CreateJobIssue request with any body
CreateJobIssueWithBodyWithResponse(ctx context.Context, jobId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateJobIssueResponse, error)
CreateJobIssueWithResponse(ctx context.Context, jobId string, body CreateJobIssueJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateJobIssueResponse, error)
}
type CreateSyncJobResponse struct {
Body []byte
HTTPResponse *http.Response
JSON201 *AnyJobResponse
JSON403 *ApiError
JSON429 *ApiError
JSON500 *ApiError
}
// Status returns HTTPResponse.Status
func (r CreateSyncJobResponse) Status() string {
if r.HTTPResponse != nil {
return r.HTTPResponse.Status
}
return http.StatusText(0)
}
// StatusCode returns HTTPResponse.StatusCode
func (r CreateSyncJobResponse) StatusCode() int {
if r.HTTPResponse != nil {
return r.HTTPResponse.StatusCode
}
return 0
}
type UpdateSyncJobResponse struct {
Body []byte
HTTPResponse *http.Response
JSON200 *AnyJobResponse
JSON403 *ApiError
JSON429 *ApiError
JSON500 *ApiError
}
// Status returns HTTPResponse.Status
func (r UpdateSyncJobResponse) Status() string {
if r.HTTPResponse != nil {
return r.HTTPResponse.Status
}
return http.StatusText(0)
}
// StatusCode returns HTTPResponse.StatusCode
func (r UpdateSyncJobResponse) StatusCode() int {
if r.HTTPResponse != nil {
return r.HTTPResponse.StatusCode
}
return 0
}
type CreateJobIssueResponse struct {
Body []byte
HTTPResponse *http.Response
JSON201 *CreateIssueResponse
JSON403 *ApiError
JSON429 *ApiError
JSON500 *ApiError
}
// Status returns HTTPResponse.Status
func (r CreateJobIssueResponse) Status() string {
if r.HTTPResponse != nil {
return r.HTTPResponse.Status
}
return http.StatusText(0)
}
// StatusCode returns HTTPResponse.StatusCode
func (r CreateJobIssueResponse) StatusCode() int {
if r.HTTPResponse != nil {
return r.HTTPResponse.StatusCode
}
return 0
}
// CreateSyncJobWithBodyWithResponse request with arbitrary body returning *CreateSyncJobResponse
func (c *ClientWithResponses) CreateSyncJobWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateSyncJobResponse, error) {
rsp, err := c.CreateSyncJobWithBody(ctx, contentType, body, reqEditors...)
if err != nil {
return nil, err
}
return ParseCreateSyncJobResponse(rsp)
}
func (c *ClientWithResponses) CreateSyncJobWithResponse(ctx context.Context, body CreateSyncJobJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateSyncJobResponse, error) {
rsp, err := c.CreateSyncJob(ctx, body, reqEditors...)
if err != nil {
return nil, err
}
return ParseCreateSyncJobResponse(rsp)
}
// UpdateSyncJobWithBodyWithResponse request with arbitrary body returning *UpdateSyncJobResponse
func (c *ClientWithResponses) UpdateSyncJobWithBodyWithResponse(ctx context.Context, jobId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateSyncJobResponse, error) {
rsp, err := c.UpdateSyncJobWithBody(ctx, jobId, contentType, body, reqEditors...)
if err != nil {
return nil, err
}
return ParseUpdateSyncJobResponse(rsp)
}
func (c *ClientWithResponses) UpdateSyncJobWithResponse(ctx context.Context, jobId string, body UpdateSyncJobJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateSyncJobResponse, error) {
rsp, err := c.UpdateSyncJob(ctx, jobId, body, reqEditors...)
if err != nil {
return nil, err
}
return ParseUpdateSyncJobResponse(rsp)
}
// CreateJobIssueWithBodyWithResponse request with arbitrary body returning *CreateJobIssueResponse
func (c *ClientWithResponses) CreateJobIssueWithBodyWithResponse(ctx context.Context, jobId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateJobIssueResponse, error) {
rsp, err := c.CreateJobIssueWithBody(ctx, jobId, contentType, body, reqEditors...)
if err != nil {
return nil, err
}
return ParseCreateJobIssueResponse(rsp)
}
func (c *ClientWithResponses) CreateJobIssueWithResponse(ctx context.Context, jobId string, body CreateJobIssueJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateJobIssueResponse, error) {
rsp, err := c.CreateJobIssue(ctx, jobId, body, reqEditors...)
if err != nil {
return nil, err
}
return ParseCreateJobIssueResponse(rsp)
}
// ParseCreateSyncJobResponse parses an HTTP response from a CreateSyncJobWithResponse call
func ParseCreateSyncJobResponse(rsp *http.Response) (*CreateSyncJobResponse, error) {
bodyBytes, err := ioutil.ReadAll(rsp.Body)
defer func() { _ = rsp.Body.Close() }()
if err != nil {
return nil, err
}
response := &CreateSyncJobResponse{
Body: bodyBytes,
HTTPResponse: rsp,
}
switch {
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201:
var dest AnyJobResponse
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON201 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403:
var dest ApiError
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON403 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429:
var dest ApiError
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON429 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500:
var dest ApiError
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON500 = &dest
}
return response, nil
}
// ParseUpdateSyncJobResponse parses an HTTP response from a UpdateSyncJobWithResponse call
func ParseUpdateSyncJobResponse(rsp *http.Response) (*UpdateSyncJobResponse, error) {
bodyBytes, err := ioutil.ReadAll(rsp.Body)
defer func() { _ = rsp.Body.Close() }()
if err != nil {
return nil, err
}
response := &UpdateSyncJobResponse{
Body: bodyBytes,
HTTPResponse: rsp,
}
switch {
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
var dest AnyJobResponse
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON200 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403:
var dest ApiError
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON403 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429:
var dest ApiError
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON429 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500:
var dest ApiError
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON500 = &dest
}
return response, nil
}
// ParseCreateJobIssueResponse parses an HTTP response from a CreateJobIssueWithResponse call
func ParseCreateJobIssueResponse(rsp *http.Response) (*CreateJobIssueResponse, error) {
bodyBytes, err := ioutil.ReadAll(rsp.Body)
defer func() { _ = rsp.Body.Close() }()
if err != nil {
return nil, err
}
response := &CreateJobIssueResponse{
Body: bodyBytes,
HTTPResponse: rsp,
}
switch {
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201:
var dest CreateIssueResponse
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON201 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403:
var dest ApiError
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON403 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429:
var dest ApiError
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON429 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500:
var dest ApiError
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON500 = &dest
}
return response, nil
}

File diff suppressed because it is too large Load Diff

21
go.mod
View File

@ -3,6 +3,8 @@ module github.com/safedep/vet
go 1.22.1
require (
buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241011110723-95b33664baad.1
buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.1-20241011110723-95b33664baad.1
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/CycloneDX/cyclonedx-go v0.9.0
github.com/anchore/syft v1.11.1
@ -19,22 +21,28 @@ require (
github.com/google/osv-scanner v1.8.4
github.com/jedib0t/go-pretty/v6 v6.5.9
github.com/kubescape/go-git-url v0.0.30
github.com/oklog/ulid/v2 v2.1.0
github.com/owenrumney/go-sarif/v2 v2.3.3
github.com/package-url/packageurl-go v0.1.3
github.com/safedep/dry v0.0.0-20240808054916-b31bac30d0ef
github.com/safedep/dry v0.0.0-20240927023913-bb455ab56626
github.com/sirupsen/logrus v1.9.3
github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82
github.com/spdx/tools-golang v0.5.5
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.9.0
golang.org/x/oauth2 v0.23.0
google.golang.org/protobuf v1.34.2
google.golang.org/grpc v1.66.0
google.golang.org/protobuf v1.35.1
gopkg.in/yaml.v2 v2.4.0
)
replace github.com/owenrumney/go-sarif/v2 v2.3.1 => github.com/safedep/go-sarif/v2 v2.3.1
// https://github.com/cli/oauth/pull/68
replace github.com/cli/oauth v1.0.1 => github.com/abhisek/oauth v1.0.1-audience
require (
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.1-20240508200655-46a4cf4ba109.1 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
github.com/CloudyKit/jet/v6 v6.2.0 // indirect
@ -88,6 +96,8 @@ require (
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.10.0 // indirect
github.com/github/go-spdx/v2 v2.3.1 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.22.0 // indirect
@ -105,6 +115,7 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
@ -145,7 +156,6 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
github.com/oklog/ulid/v2 v2.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/pborman/indent v1.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
@ -193,6 +203,10 @@ require (
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yosssi/ace v0.0.5 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/arch v0.10.0 // indirect
@ -206,7 +220,6 @@ require (
golang.org/x/time v0.6.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.66.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

144
go.sum
View File

@ -1,3 +1,17 @@
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.1-20240508200655-46a4cf4ba109.1 h1:PvjTFY+MqrYYulH74R8ddQodrEP1sThjkba8kcqO2dM=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.1-20240508200655-46a4cf4ba109.1/go.mod h1:Duw/9JoXkXIydyASnLYIiufkzySThoqavOsF+IihqvM=
buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241008111718-480f2b42b425.1 h1:ofDpCRg/yPfD0UMny+yaq4G5sHLNFhBH2DtIN9orEzQ=
buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241008111718-480f2b42b425.1/go.mod h1:DGREO91B1aVG1+zfN4gT/ZQZ0Ykcv+TZGf18SeGDcMc=
buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241009065537-4ffd9786549f.1 h1:7+y3t/2/6sEf63xCX9ErNdsuFzB0Q18PIT14aAxz5wo=
buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241009065537-4ffd9786549f.1/go.mod h1:Io32CuF5oJZDSkWJVgKvfOBTqSHnC7mqh9dpqq/PSNA=
buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241011110723-95b33664baad.1 h1:bUOILp76S06U58bp3EWI6qsoeEEBGGQJeMFdQ5Otc34=
buf.build/gen/go/safedep/api/grpc/go v1.5.1-20241011110723-95b33664baad.1/go.mod h1:LSghi5M4Z+uYLyo85Bs6lVpzit6ZoSph8YOyvpHL3mY=
buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.1-20241008111718-480f2b42b425.1 h1:IziBLru8MusSLO2lNDzSxbVxKu0dGI8zE8vG+tCMYjI=
buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.1-20241008111718-480f2b42b425.1/go.mod h1:WCxZaBpYxgWnSpauuzVhzbJawAp6uPXJPN5tbDpceQ0=
buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.1-20241009065537-4ffd9786549f.1 h1:F4fk9nhlB2ZU8odF5S/w/nS8r0htIbu6eKDKDBP+kzQ=
buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.1-20241009065537-4ffd9786549f.1/go.mod h1:WCxZaBpYxgWnSpauuzVhzbJawAp6uPXJPN5tbDpceQ0=
buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.1-20241011110723-95b33664baad.1 h1:HMED4kNSQJgrmFZXorIP1mpVbnKxzvhMgc3BOawTtGw=
buf.build/gen/go/safedep/api/protocolbuffers/go v1.35.1-20241011110723-95b33664baad.1/go.mod h1:WCxZaBpYxgWnSpauuzVhzbJawAp6uPXJPN5tbDpceQ0=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@ -71,9 +85,6 @@ github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
@ -88,6 +99,8 @@ github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azu
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/Shopify/goreferrer v0.0.0-20240724165105-aceaa0259138 h1:gjbp60h8IZQbN/TpDaYJedWbbD1h1aDPEwWnYWaDaUY=
github.com/Shopify/goreferrer v0.0.0-20240724165105-aceaa0259138/go.mod h1:NYezi6wtnJtBm5btoprXc5SvAdqH0XTXWnUup0MptAI=
github.com/abhisek/oauth v1.0.1-audience h1:nFfFPI3sRc1ofYIqt12WPFmSYSHsBBo4boLVmASiQgw=
github.com/abhisek/oauth v1.0.1-audience/go.mod h1:qd/FX8ZBD6n1sVNQO3aIdRxeu5LGw9WhKnYhIIoC2A4=
github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE=
github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY=
github.com/adrg/xdg v0.5.0 h1:dDaZvhMXatArP1NPHhnfaQUqWBLBsmx1h1HXQdMoFCY=
@ -100,22 +113,14 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anchore/clio v0.0.0-20240522144804-d81e109008aa h1:pwlAn4O9SBUnlgfa69YcqIynbUyobLVFYu8HxSoCffA=
github.com/anchore/clio v0.0.0-20240522144804-d81e109008aa/go.mod h1:nD3H5uIvjxlfmakOBgtyFQbk5Zjp3l538kxfpHPslzI=
github.com/anchore/clio v0.0.0-20240806233806-4c50c054c508 h1:dE7xKzC2eqd7QVt95jJaIbx6Kax/0GF8R1ppsXkfpCM=
github.com/anchore/clio v0.0.0-20240806233806-4c50c054c508/go.mod h1:5q0B4t18buA/F3B5cd3+CQsyuyXd+4xCirEW7t5mNTk=
github.com/anchore/fangs v0.0.0-20240508143433-f016b099950f h1:NOhzafCyNYFi88qxkBFjMzQo4dRa1vDhBzx+0Uovx8Q=
github.com/anchore/fangs v0.0.0-20240508143433-f016b099950f/go.mod h1:sVpRS2yNCw6tLVpvA1QSDVWTJVpCuAm8JNZgn4Sjz/k=
github.com/anchore/fangs v0.0.0-20240904151251-ac0148f53e5d h1:gdAg7ItS+SaNi+VZBHzVZtfLgqCXQi1uM6wGS/Q9s48=
github.com/anchore/fangs v0.0.0-20240904151251-ac0148f53e5d/go.mod h1:Xh4ObY3fmoMzOEVXwDtS1uK44JC7+nRD0n29/1KYFYg=
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537 h1:GjNGuwK5jWjJMyVppBjYS54eOiiSNv4Ba869k4wh72Q=
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537/go.mod h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8=
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a h1:nJ2G8zWKASyVClGVgG7sfM5mwoZlZ2zYpIzN2OhjWkw=
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a/go.mod h1:ubLFmlsv8/DFUQrZwY5syT5/8Er3ugSr4rDFwHsE3hg=
github.com/anchore/go-logger v0.0.0-20240217160628-ee28a485904f h1:qRQCz19ioRN2FtAct4j6Lb3Nl0VolFiuHtYMezGYBn0=
github.com/anchore/go-logger v0.0.0-20240217160628-ee28a485904f/go.mod h1:ErB21zunlmQOE/aFPkt4Tv2Q00ttFxPZ2l87gSXxSec=
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb h1:iDMnx6LIjtjZ46C0akqveX83WFzhpTD3eqOthawb5vU=
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb/go.mod h1:DmTY2Mfcv38hsHbG78xMiTDdxFtkHpgYNVDPsF2TgHk=
github.com/anchore/go-macholibre v0.0.0-20240116161251-5df1434a0b50 h1:HqAfjHHdD6P+U6iVcJl1cGYOh5YNUIffZ12fZfwMWpw=
github.com/anchore/go-macholibre v0.0.0-20240116161251-5df1434a0b50/go.mod h1:xz7uczAHBrEQ3+kIb4bnsp74JqcKpBid+gznZil1X0Q=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
@ -125,8 +130,6 @@ github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0v
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ=
github.com/anchore/packageurl-go v0.1.1-0.20240507183024-848e011fc24f h1:B/E9ixKNCasntpoch61NDaQyGPDXLEJlL+B9B/PbdbA=
github.com/anchore/packageurl-go v0.1.1-0.20240507183024-848e011fc24f/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
github.com/anchore/stereoscope v0.0.3-0.20240725180315-50ce3be7aa1f h1:xuBvotcht1Ns8IdaC4UuYV1U8MFln9c5ELeo5bzDEO8=
github.com/anchore/stereoscope v0.0.3-0.20240725180315-50ce3be7aa1f/go.mod h1:DcQdMes8SwpFli3rDH0v+Vd9qU9Jariq7JSHNJV5X/A=
github.com/anchore/stereoscope v0.0.3 h1:JRPHySy8S6P+Ff3IDiQ29ap1i8/laUQxDk9K1eFh/2U=
github.com/anchore/stereoscope v0.0.3/go.mod h1:5DJheGPjVRsSqegTB24Zi6SCHnYQnA519yeIG+RG+I4=
github.com/anchore/syft v1.11.1 h1:uJVmZ1WuhMw2cutCsBj0aUgUZxaNlbBNimZEISFttWY=
@ -150,6 +153,7 @@ github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuP
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA=
github.com/becheran/wildmatch-go v1.0.0/go.mod h1:gbMvj0NtVdJ15Mg/mH9uxk2R1QCistMyU7d9KFzroX4=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -163,8 +167,6 @@ github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx2
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg=
github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
@ -178,7 +180,6 @@ github.com/cayleygraph/quad v1.3.0 h1:xg7HOLWWPgvZ4CcvzEpfCwq42L8mzYUR+8V0jtYoBz
github.com/cayleygraph/quad v1.3.0/go.mod h1:NadtM7uMm78FskmX++XiOOrNvgkq0E1KvvhQdMseMz4=
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=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@ -198,13 +199,9 @@ github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38
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/cli/browser v1.0.0/go.mod h1:IEWkHYbLjkhtjwwWlwTHW2lGxeS5gezEQBMLTwDHf5Q=
github.com/cli/oauth v1.0.1 h1:pXnTFl/qUegXHK531Dv0LNjW4mLx626eS42gnzfXJPA=
github.com/cli/oauth v1.0.1/go.mod h1:qd/FX8ZBD6n1sVNQO3aIdRxeu5LGw9WhKnYhIIoC2A4=
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=
github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
github.com/cloudflare/circl v1.4.0 h1:BV7h5MgrktNzytKmWjpOtdYrf0lkkbF8YMlBGPhJQrY=
github.com/cloudflare/circl v1.4.0/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
@ -225,8 +222,6 @@ github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnTh
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/containerd v1.7.19 h1:/xQ4XRJ0tamDkdzrrBAUy/LE5nCcxFKdBm4EcPrSMEE=
github.com/containerd/containerd v1.7.19/go.mod h1:h4FtNYUUMB4Phr6v+xG89RYKj9XccvbNSCKjdufCrkc=
github.com/containerd/containerd v1.7.21 h1:USGXRK1eOC/SX0L195YgxTHb0a00anxajOzgfN0qrCA=
github.com/containerd/containerd v1.7.21/go.mod h1:e3Jz1rYRUZ2Lt51YrH9Rz0zPyJBOlSvB3ghr2jbVD8g=
github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA=
@ -269,28 +264,20 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v27.2.0+incompatible h1:yHD1QEB1/0vr5eBNpu8tncu8gWxg8EydFPOSKHzXSMM=
github.com/docker/cli v27.2.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v27.1.2+incompatible h1:AhGzR1xaQIy53qCkxARaFluI00WPGtXn0AJuoQsVYTY=
github.com/docker/docker v27.1.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo=
github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
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-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dop251/goja v0.0.0-20240806095544-3491d4a58fbe h1:jwFJkgsdelB87ohlXaAGSd05Cb5ALDFa9iW9IGRHcRM=
github.com/dop251/goja v0.0.0-20240806095544-3491d4a58fbe/go.mod h1:DF+w/nLMIkvRpyhd/0K+Okbh3fVZBtXLwRtS/ccAa5w=
github.com/dop251/goja v0.0.0-20240828124009-016eb7256539 h1:YIxvsQAoCLGScK2c9ag+4sFCgiQFpMzywJG6dQZFu9k=
github.com/dop251/goja v0.0.0-20240828124009-016eb7256539/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4=
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
@ -305,8 +292,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI=
github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0=
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 h1:IgJPqnrlY2Mr4pYB6oaMKvFvwJ9H+X6CCY5x1vCTcpc=
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155/go.mod h1:5Wkq+JduFtdAXihLmeTJf+tRYIT4KBc2vPXDhwVo1pA=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
@ -323,7 +310,6 @@ github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA=
github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
@ -345,13 +331,22 @@ github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/github/go-spdx/v2 v2.3.1 h1:ffGuHTbHuHzWPt53n8f9o8clGutuLPObo3zB4JAjxU8=
github.com/github/go-spdx/v2 v2.3.1/go.mod h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ=
github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvpAv8=
github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
github.com/gkampitakis/go-snaps v0.5.7 h1:uVGjHR4t4pPHU944udMx7VKHpwepZXmvDMF+yDmI0rg=
github.com/gkampitakis/go-snaps v0.5.7/go.mod h1:ZABkO14uCuVxBHAXAfKG+bqNz+aa1bGPAg8jkI0Nk8Y=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
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-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/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
@ -464,8 +459,6 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/osv-scanner v1.8.3 h1:TVPk2aWhPK+bjp9ZPdGRphG/pwRq4nlyBqHsfQV3keQ=
github.com/google/osv-scanner v1.8.3/go.mod h1:O6tZsJMb/QYXzLjnxHBNLutjsdm8tsrmljiq1WxW8u4=
github.com/google/osv-scanner v1.8.4 h1:CScR1b8GqvgbUwG5+bvOlyeE8dtl4zNmh3IDlJE2ZeY=
github.com/google/osv-scanner v1.8.4/go.mod h1:ImQyo6WCoZ10iI20zD+iqt0g8cS5gJegqN0ngT1x53M=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -484,8 +477,6 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/pprof v0.0.0-20240903155634-a8630aee4ab9 h1:q5g0N9eal4bmJwXHC5z0QCKs8qhS35hFfq0BAYsIwZI=
github.com/google/pprof v0.0.0-20240903155634-a8630aee4ab9/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
@ -506,6 +497,8 @@ github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
@ -603,7 +596,6 @@ github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
@ -638,6 +630,8 @@ github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqA
github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@ -691,9 +685,10 @@ github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -705,7 +700,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
@ -719,8 +713,8 @@ github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bl
github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/owenrumney/go-sarif v1.1.1 h1:QNObu6YX1igyFKhdzd7vgzmw7XsWN3/6NMGuDzBgXmE=
github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U=
github.com/owenrumney/go-sarif/v2 v2.3.3 h1:ubWDJcF5i3L/EIOER+ZyQ03IfplbSU1BLOE26uKQIIU=
github.com/owenrumney/go-sarif/v2 v2.3.3/go.mod h1:MSqMMx9WqlBSY7pXoOZWgEsVB4FDNfhcaXDA1j6Sr+w=
@ -736,13 +730,9 @@ github.com/pborman/indent v1.2.1/go.mod h1:FitS+t35kIYtB5xWTZAPhnmrxcciEEOdbyrrp
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.19 h1:tYLzDnjDXh9qIxSTKHwXwOYmm9d887Y7Y1ZkyXYHAN4=
github.com/pierrec/lz4/v4 v4.1.19/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/piprate/json-gold v0.5.0 h1:RmGh1PYboCFcchVFuh2pbSWAZy4XJaqTMU4KQYsApbM=
@ -754,6 +744,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -764,8 +756,6 @@ github.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQ
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
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=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4=
github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@ -776,8 +766,6 @@ github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJoX0=
github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
@ -799,11 +787,9 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
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/safedep/dry v0.0.0-20240808054916-b31bac30d0ef h1:liYqq19DtflpN4gX2aaGUN0XJJYC/xnhx703GR+CJYU=
github.com/safedep/dry v0.0.0-20240808054916-b31bac30d0ef/go.mod h1:OHfFlBHlJsIJyK3vsc40yaInGUxrzPhwYu8hM1h+kxs=
github.com/safedep/dry v0.0.0-20240927023913-bb455ab56626 h1:0WadRINp2CAx5AvZkix9uFsibU9PXTtmoass7H+Z+9w=
github.com/safedep/dry v0.0.0-20240927023913-bb455ab56626/go.mod h1:OHfFlBHlJsIJyK3vsc40yaInGUxrzPhwYu8hM1h+kxs=
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
@ -826,8 +812,6 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smacker/go-tree-sitter v0.0.0-20240625050157-a31a98a7c0f6 h1:mtD4ESyObQZnRVxHFcaYp2d7jMBDa4WJRXSB1Vszj+A=
github.com/smacker/go-tree-sitter v0.0.0-20240625050157-a31a98a7c0f6/go.mod h1:q99oHDsbP0xRwmn7Vmob8gbSMNyvJ83OauXPSuHQuKE=
github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82 h1:6C8qej6f1bStuePVkLSFxoU22XBS165D3klxlzRg8F4=
github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82/go.mod h1:xe4pgH49k4SsmkQq5OT8abwhWmnzkhpgnXeekbx2efw=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
@ -845,8 +829,6 @@ github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
@ -875,7 +857,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
@ -899,6 +880,14 @@ github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uR
github.com/terminalstatic/go-xsd-validate v0.1.5/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw=
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94=
github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
@ -966,6 +955,8 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
@ -976,16 +967,16 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8=
golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -1001,8 +992,6 @@ 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.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -1015,8 +1004,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
@ -1048,8 +1035,6 @@ 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.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1099,8 +1084,6 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -1120,8 +1103,6 @@ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
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.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1203,6 +1184,7 @@ 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-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-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -1214,8 +1196,6 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.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.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -1223,8 +1203,6 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1239,8 +1217,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1265,6 +1241,7 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/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-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@ -1370,6 +1347,7 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
@ -1414,12 +1392,8 @@ google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ6
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
google.golang.org/genproto/googleapis/api v0.0.0-20240812133136-8ffd90a71988 h1:+/tmTy5zAieooKIXfzDm9KiA3Bv6JBwriRN9LY+yayk=
google.golang.org/genproto/googleapis/api v0.0.0-20240812133136-8ffd90a71988/go.mod h1:4+X6GvPs+25wZKbQq9qyAXrwIRExv7w0Ea6MgZLZiDM=
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc=
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988 h1:V71AcdLZr2p8dC9dbOIMCpqi4EmRl8wUwnJzXXLmbmc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@ -1449,8 +1423,6 @@ 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.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c=
google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
@ -1467,8 +1439,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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -1504,8 +1476,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3 h1:b2FmK8YH+QEwq/Sy2uAEhmqL5nPfGYbJOcaqjeYYZoA=
k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=

View File

@ -5,30 +5,44 @@ import (
"os"
"path/filepath"
"strconv"
"time"
"gopkg.in/yaml.v2"
)
const (
apiUrlEnvKey = "VET_INSIGHTS_API_URL"
apiKeyEnvKey = "VET_INSIGHTS_API_KEY"
apiKeyAlternateEnvKey = "VET_API_KEY"
communityModeEnvKey = "VET_COMMUNITY_MODE"
apiUrlEnvKey = "VET_INSIGHTS_API_URL"
apiKeyEnvKey = "VET_API_KEY"
apiKeyAlternateEnvKey = "VET_INSIGHTS_API_KEY"
communityModeEnvKey = "VET_COMMUNITY_MODE"
controlTowerTenantEnvKey = "VET_CONTROL_TOWER_TENANT_ID"
defaultApiUrl = "https://api.safedep.io/insights/v1"
defaultCommunityApiUrl = "https://api.safedep.io/insights-community/v1"
defaultControlPlaneApiUrl = "https://api.safedep.io/control-plane/v1"
defaultSyncApiUrl = "https://api.safedep.io/sync/v1"
defaultApiUrl = "https://api.safedep.io/insights/v1"
defaultCommunityApiUrl = "https://api.safedep.io/insights-community/v1"
// gRPC service base URL.
defaultSyncApiUrl = "https://api.safedep.io"
defaultControlPlaneApiUrl = "https://cloud.safedep.io"
homeRelativeConfigPath = ".safedep/vet-auth.yml"
cloudIdentityServiceClientId = "QtXHUN3hOdbJbCiGU8FiNCnC2KtuROCu" // gitleaks:allow
cloudIdentityServiceAudience = "https://cloud.safedep.io"
cloudIdentityServiceBaseUrl = "https://auth.safedep.io"
cloudIdentityServiceDeviceCodeUrl = "https://auth.safedep.io/oauth/device/code"
cloudIdentityServiceTokenUrl = "https://auth.safedep.io/oauth/token"
)
type Config struct {
ApiUrl string `yaml:"api_url"`
ApiKey string `yaml:"api_key"`
Community bool `yaml:"community"`
ControlPlaneApiUrl string `yaml:"cp_api_url"`
SyncApiUrl string `yaml:"sync_api_url"`
ApiUrl string `yaml:"api_url"`
ApiKey string `yaml:"api_key"`
Community bool `yaml:"community"`
ControlPlaneApiUrl string `yaml:"control_api_url"`
SyncApiUrl string `yaml:"sync_api_url"`
TenantDomain string `yaml:"tenant_domain"`
CloudAccessToken string `yaml:"cloud_access_token"`
CloudRefreshToken string `yaml:"cloud_refresh_token"`
CloudAccessTokenUpdatedAt time.Time `yaml:"cloud_access_token_updated_at"`
}
// Global config to be used during runtime
@ -38,8 +52,58 @@ func init() {
loadConfiguration()
}
func Configure(m Config) error {
globalConfig = &m
func DefaultConfig() Config {
return Config{
ApiUrl: defaultApiUrl,
Community: false,
ControlPlaneApiUrl: defaultControlPlaneApiUrl,
SyncApiUrl: defaultSyncApiUrl,
}
}
func PersistApiKey(key, domain string) error {
if globalConfig == nil {
c := DefaultConfig()
globalConfig = &c
}
if domain != "" {
globalConfig.TenantDomain = domain
}
globalConfig.ApiUrl = defaultApiUrl
globalConfig.ApiKey = key
return persistConfiguration()
}
func PersistCloudTokens(accessToken, refreshToken, domain string) error {
if globalConfig == nil {
c := DefaultConfig()
globalConfig = &c
}
// We are explicitly check for empty string for domain
// because we do not want to overwrite the domain if it is
// not provided.
if domain != "" {
globalConfig.TenantDomain = domain
}
globalConfig.CloudAccessToken = accessToken
globalConfig.CloudRefreshToken = refreshToken
globalConfig.CloudAccessTokenUpdatedAt = time.Now()
return persistConfiguration()
}
func PersistTenantDomain(domain string) error {
if globalConfig == nil {
c := DefaultConfig()
globalConfig = &c
}
globalConfig.TenantDomain = domain
return persistConfiguration()
}
@ -51,15 +115,43 @@ func DefaultCommunityApiUrl() string {
return defaultCommunityApiUrl
}
func DefaultControlPlaneApiUrl() string {
if (globalConfig != nil) && (globalConfig.ControlPlaneApiUrl != "") {
return globalConfig.ControlPlaneApiUrl
}
return defaultControlPlaneApiUrl
func CloudIdentityServiceClientId() string {
return cloudIdentityServiceClientId
}
func DefaultSyncApiUrl() string {
func CloudIdentityServiceBaseUrl() string {
return cloudIdentityServiceBaseUrl
}
func CloudIdentityServiceDeviceCodeUrl() string {
return cloudIdentityServiceDeviceCodeUrl
}
func CloudIdentityServiceAudience() string {
return cloudIdentityServiceAudience
}
func CloudIdentityServiceTokenUrl() string {
return cloudIdentityServiceTokenUrl
}
func CloudAccessToken() string {
if globalConfig != nil {
return globalConfig.CloudAccessToken
}
return ""
}
func CloudRefreshToken() string {
if globalConfig != nil {
return globalConfig.CloudRefreshToken
}
return ""
}
func SyncApiUrl() string {
if (globalConfig != nil) && (globalConfig.SyncApiUrl != "") {
return globalConfig.SyncApiUrl
}
@ -67,19 +159,40 @@ func DefaultSyncApiUrl() string {
return defaultSyncApiUrl
}
func ControlTowerUrl() string {
if (globalConfig != nil) && (globalConfig.ControlPlaneApiUrl != "") {
return globalConfig.ControlPlaneApiUrl
}
return defaultControlPlaneApiUrl
}
func TenantDomain() string {
tenantFromEnv := os.Getenv(controlTowerTenantEnvKey)
if tenantFromEnv != "" {
return tenantFromEnv
}
if globalConfig != nil {
return globalConfig.TenantDomain
}
return ""
}
func ApiUrl() string {
if url, ok := os.LookupEnv(apiUrlEnvKey); ok {
return url
}
if globalConfig != nil {
return globalConfig.ApiUrl
}
if CommunityMode() {
return defaultCommunityApiUrl
}
if globalConfig != nil {
return globalConfig.ApiUrl
}
return defaultApiUrl
}
@ -118,6 +231,14 @@ func SetRuntimeCommunityMode() {
os.Setenv(communityModeEnvKey, "true")
}
func SetRuntimeCloudTenant(domain string) {
os.Setenv(controlTowerTenantEnvKey, domain)
}
func SetRuntimeApiKey(key string) {
os.Setenv(apiKeyEnvKey, key)
}
func loadConfiguration() error {
path, err := os.UserHomeDir()
if err != nil {

54
internal/auth/grpc.go Normal file
View File

@ -0,0 +1,54 @@
package auth
import (
"fmt"
"net/http"
"net/url"
"os"
"github.com/safedep/vet/pkg/common/logger"
"google.golang.org/grpc"
drygrpc "github.com/safedep/dry/adapters/grpc"
)
// Create a gRPC client connection for the control plane
// based on available configuration
func ControlPlaneClientConnection(name string) (*grpc.ClientConn, error) {
return cloudClientConnection(name, ControlTowerUrl(), CloudAccessToken())
}
func SyncClientConnection(name string) (*grpc.ClientConn, error) {
return cloudClientConnection(name, SyncApiUrl(), ApiKey())
}
func cloudClientConnection(name, loc, tok string) (*grpc.ClientConn, error) {
parsedUrl, err := url.Parse(loc)
if err != nil {
return nil, err
}
host, port := parsedUrl.Hostname(), parsedUrl.Port()
if port == "" {
port = "443"
}
logger.Debugf("Establishing grpc connection for: %s host: %s, port: %s",
name, host, port)
headers := http.Header{}
headers.Set("x-tenant-id", TenantDomain())
vetTenantMockUser := os.Getenv("VET_CONTROL_TOWER_MOCK_USER")
if vetTenantMockUser != "" {
headers.Set("x-mock-user", vetTenantMockUser)
}
client, err := drygrpc.GrpcClient(name, host, port,
tok, headers, []grpc.DialOption{})
if err != nil {
return nil, fmt.Errorf("failed to create gRPC client: %w", err)
}
return client, nil
}

View File

@ -1,77 +0,0 @@
package auth
import (
"context"
"errors"
"fmt"
"net/http"
"time"
"github.com/deepmap/oapi-codegen/pkg/types"
"github.com/safedep/dry/utils"
"github.com/safedep/vet/gen/cpv1trials"
"github.com/safedep/vet/pkg/common/logger"
apierr "github.com/safedep/dry/errors"
)
type TrialConfig struct {
Email string
ControlPlaneApiUrl string
}
type trialRegistrationResponse struct {
Id string
ExpiresAt time.Time
}
type trialRegistrationClient struct {
config TrialConfig
}
func NewTrialRegistrationClient(config TrialConfig) *trialRegistrationClient {
return &trialRegistrationClient{config: config}
}
func (client *trialRegistrationClient) Execute() (*trialRegistrationResponse, error) {
if utils.IsEmptyString(client.config.Email) {
return nil, errors.New("email is required")
}
if utils.IsEmptyString(client.config.ControlPlaneApiUrl) {
return nil, errors.New("control plane API is required")
}
logger.Infof("Trial registrations using Control Plane: %s",
client.config.ControlPlaneApiUrl)
cpClient, err := cpv1trials.NewClientWithResponses(client.config.ControlPlaneApiUrl)
if err != nil {
return nil, err
}
logger.Infof("Trial registration requesting API key for: %s",
client.config.Email)
res, err := cpClient.RegisterTrialUserWithResponse(context.Background(),
cpv1trials.RegisterTrialUserJSONRequestBody{
Email: types.Email(client.config.Email),
})
if err != nil {
return nil, err
}
if res.HTTPResponse.StatusCode != http.StatusCreated {
if err, ok := apierr.UnmarshalApiError(res.Body); ok {
return nil, err
} else {
return nil, fmt.Errorf("unexpected status code:%d from control plane",
res.HTTPResponse.StatusCode)
}
}
return &trialRegistrationResponse{
Id: utils.SafelyGetValue(res.JSON201.Id),
ExpiresAt: utils.SafelyGetValue(res.JSON201.ExpiresAt),
}, nil
}

View File

@ -1,62 +1,24 @@
package auth
import (
"context"
"fmt"
"net/http"
import "github.com/safedep/vet/pkg/cloud"
apierr "github.com/safedep/dry/errors"
"github.com/safedep/dry/utils"
"github.com/safedep/vet/gen/cpv1"
"github.com/safedep/vet/pkg/common/logger"
)
type VerifyConfig struct {
ControlPlaneApiUrl string
}
// Verify function takes config and current API key available
// from this package and returns an error if auth is invalid
func Verify(config *VerifyConfig) error {
if CommunityMode() {
logger.Infof("Skipping auth verify due to community mode enabled")
return nil
}
logger.Infof("Verifying auth token using Control Plane: %s", config.ControlPlaneApiUrl)
client, err := cpv1.NewClientWithResponses(config.ControlPlaneApiUrl)
// Verify authentication to the data plane using
// API key and Ping Service.
func Verify() error {
conn, err := SyncClientConnection("vet-auth-verify")
if err != nil {
return err
}
authKeyApplier := func(ctx context.Context, req *http.Request) error {
req.Header.Set("Authorization", ApiKey())
return nil
}
resp, err := client.GetApiCredentialIntrospectionWithResponse(context.Background(),
authKeyApplier)
pingService, err := cloud.NewPingService(conn)
if err != nil {
return err
}
if resp.StatusCode() != http.StatusOK {
if err, ok := apierr.UnmarshalApiError(resp.Body); ok {
return err
} else {
return fmt.Errorf("unexpected status code:%d from control plane",
resp.HTTPResponse.StatusCode)
}
_, err = pingService.Ping()
if err != nil {
return err
}
if resp.JSON200 == nil {
return fmt.Errorf("invalid nil response from server")
}
logger.Infof("Current auth token is valid with expiry: %s",
utils.SafelyGetValue(resp.JSON200.Expiry))
return nil
}

17
main.go
View File

@ -7,6 +7,7 @@ import (
"strconv"
"github.com/safedep/dry/utils"
"github.com/safedep/vet/cmd/cloud"
"github.com/safedep/vet/internal/ui"
"github.com/safedep/vet/pkg/common/logger"
"github.com/safedep/vet/pkg/exceptions"
@ -23,17 +24,10 @@ var (
)
var banner string = `
.----------------. .----------------. .----------------.
| .--------------. || .--------------. || .--------------. |
| | ____ ____ | || | _________ | || | _________ | |
| ||_ _| |_ _| | || | |_ ___ | | || | | _ _ | | |
| | \ \ / / | || | | |_ \_| | || | |_/ | | \_| | |
| | \ \ / / | || | | _| _ | || | | | | |
| | \ ' / | || | _| |___/ | | || | _| |_ | |
| | \_/ | || | |_________| | || | |_____| | |
| | | || | | || | | |
| '--------------' || '--------------' || '--------------' |
'----------------' '----------------' '----------------'
Yb dP 888888 888888
Yb dP 88__ 88
YbdP 88"" 88
YP 888888 88
`
@ -64,6 +58,7 @@ func main() {
cmd.AddCommand(newCodeCommand())
cmd.AddCommand(newVersionCommand())
cmd.AddCommand(newConnectCommand())
cmd.AddCommand(cloud.NewCloudCommand())
cobra.OnInitialize(func() {
printBanner()

3
pkg/cloud/cloud.go Normal file
View File

@ -0,0 +1,3 @@
// Package cloud contains the services for interacting with SafeDep
// Cloud. It uses SafeDep gRPC API for communication.
package cloud

47
pkg/cloud/key.go Normal file
View File

@ -0,0 +1,47 @@
package cloud
import (
"context"
"time"
"buf.build/gen/go/safedep/api/grpc/go/safedep/services/controltower/v1/controltowerv1grpc"
controltowerv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/services/controltower/v1"
"google.golang.org/grpc"
)
type apiKeyService struct {
conn *grpc.ClientConn
}
func NewApiKeyService(conn *grpc.ClientConn) (*apiKeyService, error) {
return &apiKeyService{conn: conn}, nil
}
type CreateApiKeyRequest struct {
Name string
Desc string
ExpiryInDays int
}
type CreateApiKeyResponse struct {
Key string
ExpiresAt time.Time
}
func (a *apiKeyService) CreateApiKey(req *CreateApiKeyRequest) (*CreateApiKeyResponse, error) {
keyService := controltowerv1grpc.NewApiKeyServiceClient(a.conn)
res, err := keyService.CreateApiKey(context.Background(), &controltowerv1.CreateApiKeyRequest{
Name: req.Name,
Description: &req.Desc,
ExpiryDays: int32(req.ExpiryInDays),
})
if err != nil {
return nil, err
}
return &CreateApiKeyResponse{
Key: res.GetKey(),
ExpiresAt: res.GetExpiresAt().AsTime(),
}, nil
}

46
pkg/cloud/onboarding.go Normal file
View File

@ -0,0 +1,46 @@
package cloud
import (
"context"
"buf.build/gen/go/safedep/api/grpc/go/safedep/services/controltower/v1/controltowerv1grpc"
controltowerv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/services/controltower/v1"
"google.golang.org/grpc"
)
type onboardingService struct {
conn *grpc.ClientConn
}
type RegisterRequest struct {
Email string
Name string
OrgName string
OrgDomain string
}
type RegisterResponse struct {
TenantDomain string
}
func NewOnboardingService(conn *grpc.ClientConn) (*onboardingService, error) {
return &onboardingService{conn}, nil
}
func (s *onboardingService) Register(req *RegisterRequest) (*RegisterResponse, error) {
onbService := controltowerv1grpc.NewOnboardingServiceClient(s.conn)
res, err := onbService.OnboardUser(context.Background(), &controltowerv1.OnboardUserRequest{
Email: req.Email,
Name: req.Name,
OrganizationName: req.OrgName,
OrganizationDomain: req.OrgDomain,
})
if err != nil {
return nil, err
}
return &RegisterResponse{
TenantDomain: res.GetTenant().GetDomain(),
}, nil
}

42
pkg/cloud/ping.go Normal file
View File

@ -0,0 +1,42 @@
package cloud
import (
"context"
"time"
"buf.build/gen/go/safedep/api/grpc/go/safedep/services/controltower/v1/controltowerv1grpc"
controltowerv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/services/controltower/v1"
"github.com/oklog/ulid/v2"
"google.golang.org/grpc"
)
type pingService struct {
conn *grpc.ClientConn
}
type PingResponse struct {
StartedAt time.Time
FinishedAt time.Time
}
func NewPingService(conn *grpc.ClientConn) (*pingService, error) {
return &pingService{conn: conn}, nil
}
func (p *pingService) Ping() (*PingResponse, error) {
pr := PingResponse{
StartedAt: time.Now(),
}
pingService := controltowerv1grpc.NewPingServiceClient(p.conn)
_, err := pingService.Ping(context.Background(), &controltowerv1.PingRequest{
Id: ulid.MustNew(ulid.Now(), nil).String(),
})
if err != nil {
return nil, err
}
pr.FinishedAt = time.Now()
return &pr, nil
}

View File

@ -0,0 +1,42 @@
package query
import (
"context"
"buf.build/gen/go/safedep/api/grpc/go/safedep/services/controltower/v1/controltowerv1grpc"
controltowerv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/services/controltower/v1"
"google.golang.org/grpc"
)
type queryService struct {
client *grpc.ClientConn
}
func NewQueryService(client *grpc.ClientConn) (*queryService, error) {
return &queryService{client: client}, nil
}
func (q *queryService) ExecuteSql(sql string) (*QueryResponse, error) {
queryServiceClient := controltowerv1grpc.NewQueryServiceClient(q.client)
res, err := queryServiceClient.QueryBySql(context.Background(), &controltowerv1.QueryBySqlRequest{
Query: sql,
PageSize: 100,
})
if err != nil {
return nil, err
}
var response QueryResponse
for _, row := range res.Rows {
rowMap := make(map[string]interface{})
for key, val := range row.Fields {
rowMap[key] = val.GetStringValue()
}
response = append(response, rowMap)
}
return &response, nil
}

View File

@ -0,0 +1,48 @@
package query
type QueryRow map[string]interface{}
type QueryResponse []QueryRow
func (q *QueryResponse) Count() int {
if q == nil {
return 0
}
return len(*q)
}
func (q *QueryResponse) GetRow(index int) *QueryRow {
if q.Count() <= index {
return nil
}
return &(*q)[index]
}
func (q *QueryResponse) ForEachRow(fn func(*QueryRow)) {
if q == nil {
return
}
for _, row := range *q {
fn(&row)
}
}
func (q *QueryRow) ForEachField(fn func(string, interface{})) {
if q == nil {
return
}
for key, val := range *q {
fn(key, val)
}
}
func (q *QueryRow) GetField(key string) interface{} {
if q == nil {
return nil
}
return (*q)[key]
}

27
pkg/cloud/user.go Normal file
View File

@ -0,0 +1,27 @@
package cloud
import (
"context"
"buf.build/gen/go/safedep/api/grpc/go/safedep/services/controltower/v1/controltowerv1grpc"
controltowerv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/services/controltower/v1"
"google.golang.org/grpc"
)
type userService struct {
conn *grpc.ClientConn
}
func NewUserService(conn *grpc.ClientConn) (*userService, error) {
return &userService{conn: conn}, nil
}
func (s *userService) CurrentUserInfo() (*controltowerv1.GetUserInfoResponse, error) {
userService := controltowerv1grpc.NewUserServiceClient(s.conn)
res, err := userService.GetUserInfo(context.Background(), &controltowerv1.GetUserInfoRequest{})
if err != nil {
return nil, err
}
return res, nil
}

View File

@ -3,10 +3,12 @@ package models
import (
"fmt"
"hash/fnv"
"path/filepath"
"strconv"
"strings"
"sync"
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
"github.com/google/osv-scanner/pkg/lockfile"
"github.com/safedep/vet/gen/insightapi"
@ -28,17 +30,59 @@ const (
EcosystemSpdxSBOM = "SpdxSbom"
)
type ManifestSourceType string
const (
ManifestSourceLocal = ManifestSourceType("local")
ManifestSourceGitRepository = ManifestSourceType("git_repository")
)
// We now have different sources from where a package
// manifest can be identified. For example, local, github,
// and may be in future within containers or archives like
// JAR. So we need to store additional internal metadata
type PackageManifestSource struct {
// The source type of this package namespace
Type ManifestSourceType
// The namespace of the package manifest. Examples:
// - Directory when source is local
// - GitHub repo URL when source is GitHub
Namespace string
// The namespace relative path of the package manifest
Path string
// Explicit override the display path
DisplayPath string
}
func (ps PackageManifestSource) GetDisplayPath() string {
switch ps.Type {
case ManifestSourceLocal:
return filepath.Join(ps.Namespace, ps.Path)
default:
return ps.DisplayPath
}
}
func (ps PackageManifestSource) GetNamespace() string {
return ps.Namespace
}
func (ps PackageManifestSource) GetPath() string {
return ps.Path
}
// Represents a package manifest that contains a list
// of packages. Example: pom.xml, requirements.txt
type PackageManifest struct {
// The source of the package manifest
Source PackageManifestSource `json:"source"`
// Filesystem path of this manifest
Path string `json:"path"`
// When we scan non-path entities like Github org / repo
// then only path doesn't make sense, which is more local
// temporary file path
DisplayPath string `json:"display_path"`
// Ecosystem to interpret this manifest
Ecosystem string `json:"ecosystem"`
@ -52,8 +96,30 @@ type PackageManifest struct {
m sync.Mutex
}
// Deprecated: Use NewPackageManifest* initializers
func NewPackageManifest(path, ecosystem string) *PackageManifest {
return NewPackageManifestFromLocal(path, ecosystem)
}
func NewPackageManifestFromLocal(path, ecosystem string) *PackageManifest {
return newPackageManifest(PackageManifestSource{
Type: ManifestSourceLocal,
Namespace: filepath.Dir(path),
Path: filepath.Base(path),
}, path, ecosystem)
}
func NewPackageManifestFromGitHub(repo, repoRelativePath, realPath, ecosystem string) *PackageManifest {
return newPackageManifest(PackageManifestSource{
Type: ManifestSourceGitRepository,
Namespace: repo,
Path: repoRelativePath,
}, realPath, ecosystem)
}
func newPackageManifest(source PackageManifestSource, path, ecosystem string) *PackageManifest {
return &PackageManifest{
Source: source,
Path: path,
Ecosystem: ecosystem,
Packages: make([]*Package, 0),
@ -61,6 +127,16 @@ func NewPackageManifest(path, ecosystem string) *PackageManifest {
}
}
// Parsers usually create a package manifest from file, readers
// have the context to set the source correct. Example: GitHub reader
func (p *PackageManifest) UpdateSourceAsGitRepository(repo, repoRelativePath string) {
p.Source = PackageManifestSource{
Type: ManifestSourceGitRepository,
Namespace: repo,
Path: repoRelativePath,
}
}
func (pm *PackageManifest) AddPackage(pkg *Package) {
pm.m.Lock()
defer pm.m.Unlock()
@ -73,22 +149,22 @@ func (pm *PackageManifest) AddPackage(pkg *Package) {
pm.DependencyGraph.AddNode(pkg)
}
func (pm *PackageManifest) GetSource() PackageManifestSource {
return pm.Source
}
func (pm *PackageManifest) GetPath() string {
return pm.Path
}
func (pm *PackageManifest) SetDisplayPath(path string) {
pm.DisplayPath = path
pm.Source.DisplayPath = path
}
// GetDisplayPath returns the [DisplayPath] if available or fallsback
// to [Path]
func (pm *PackageManifest) GetDisplayPath() string {
if len(pm.DisplayPath) > 0 {
return pm.DisplayPath
}
return pm.GetPath()
return pm.Source.GetDisplayPath()
}
// GetPackages returns the list of packages in this manifest
@ -111,6 +187,25 @@ func (pm *PackageManifest) GetPackagesCount() int {
return len(pm.GetPackages())
}
func (pm *PackageManifest) GetControlTowerSpecEcosystem() packagev1.Ecosystem {
switch pm.Ecosystem {
case EcosystemCargo:
return packagev1.Ecosystem_ECOSYSTEM_CARGO
case EcosystemGo:
return packagev1.Ecosystem_ECOSYSTEM_GO
case EcosystemMaven:
return packagev1.Ecosystem_ECOSYSTEM_MAVEN
case EcosystemNpm:
return packagev1.Ecosystem_ECOSYSTEM_NPM
case EcosystemRubyGems:
return packagev1.Ecosystem_ECOSYSTEM_RUBYGEMS
case EcosystemPyPI:
return packagev1.Ecosystem_ECOSYSTEM_PYPI
default:
return packagev1.Ecosystem_ECOSYSTEM_UNSPECIFIED
}
}
func (pm *PackageManifest) GetSpecEcosystem() modelspec.Ecosystem {
switch pm.Ecosystem {
case EcosystemCargo:
@ -216,6 +311,37 @@ func (p *Package) DependencyPath() []*Package {
return dg.PathToRoot(p)
}
func (p *Package) GetDependencies() ([]*Package, error) {
graph := p.GetDependencyGraph()
if graph == nil {
return nil, fmt.Errorf("dependency graph not available")
}
dependencies := []*Package{}
nodes := graph.GetNodes()
for _, node := range nodes {
if node.Root {
continue
}
if node.Data == nil {
continue
}
if p.GetName() != node.Data.GetName() &&
p.GetVersion() != node.Data.GetVersion() &&
p.GetSpecEcosystem() != node.Data.GetSpecEcosystem() {
continue
}
dependencies = append(dependencies, node.Children...)
break
}
return dependencies, nil
}
func NewPackageDetail(ecosystem, name, version string) lockfile.PackageDetails {
return lockfile.PackageDetails{
Ecosystem: lockfile.Ecosystem(ecosystem),

View File

@ -129,7 +129,9 @@ func (p *githubReader) processTopLevelLockfiles(ctx context.Context, client *git
return err
}
pm.UpdateSourceAsGitRepository(gitUrl.GetHttpCloneURL(), entry.GetPath())
pm.SetDisplayPath(entry.GetURL())
err = handler(pm, NewManifestModelReader(pm))
if err != nil {
return err
@ -179,6 +181,7 @@ func (p *githubReader) processRemoteDependencyGraph(ctx context.Context, client
// Override the display path because local path of the downloaded
// SBOM does not actually have a meaning
manifest.UpdateSourceAsGitRepository(gitUrl.GetHttpCloneURL(), "<github-dependency-graph>")
manifest.SetDisplayPath(gitUrl.GetHttpCloneURL())
return handler(manifest, NewManifestModelReader(manifest))

42
pkg/reporter/ci/ci.go Normal file
View File

@ -0,0 +1,42 @@
package ci
import "net/url"
type gitRefType string
const (
Branch = gitRefType("branch")
Tag = gitRefType("tag")
PullRequest = gitRefType("pull_request")
)
// Introspector defines a contract for implementing
// runtime information collector from various CI environments.
// This is modeled based on the default environments in GHA. But
// we will keep this minimal and add extended interfaces for
// additional CI specific information.
// https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables
type Introspector interface {
// Returns the URL of the repository for which the CI job is running
GetRepositoryURL() (url.URL, error)
// Returns the repository name for which the CI job is running
GetRepositoryName() (string, error)
// Returns the event that triggered the CI job
GetEvent() (gitRefType, error)
// Returns the GitRef for which the CI job is running
// This is fulled formed e.g. refs/heads/master
GetGitRef() (string, error)
// Get the ref name for which the CI job is running
// This is the short form of the GitRef e.g. master
GetRefName() (string, error)
// Get the git ref type for which the CI job is running
GetRefType() (string, error)
// GitSHA returns the commit SHA for which the CI job is running
GetGitSHA() (string, error)
}

1
pkg/reporter/ci/git.go Normal file
View File

@ -0,0 +1 @@
package ci

View File

@ -34,12 +34,16 @@ var events []analyzer.AnalyzerEvent = []analyzer.AnalyzerEvent{
CheckType: checks.CheckType_CheckTypeLicense,
},
Manifest: &models.PackageManifest{
DisplayPath: "displayPath1",
Source: models.PackageManifestSource{
DisplayPath: "displayPath1",
},
},
Package: &models.Package{
PackageDetails: models.NewPackageDetail("ecosystem1", "name1", "version1"),
Manifest: &models.PackageManifest{
DisplayPath: "displayPath1",
Source: models.PackageManifestSource{
DisplayPath: "displayPath1",
},
},
Insights: &insightapi.PackageVersionInsight{
Licenses: &licenses,
@ -55,12 +59,16 @@ var events []analyzer.AnalyzerEvent = []analyzer.AnalyzerEvent{
CheckType: checks.CheckType_CheckTypeVulnerability,
},
Manifest: &models.PackageManifest{
DisplayPath: "displayPath2",
Source: models.PackageManifestSource{
DisplayPath: "displayPath1",
},
},
Package: &models.Package{
PackageDetails: models.NewPackageDetail("ecosystem2", "name2", "version2"),
Manifest: &models.PackageManifest{
DisplayPath: "displayPath2",
Source: models.PackageManifestSource{
DisplayPath: "displayPath1",
},
},
Insights: &insightapi.PackageVersionInsight{
Vulnerabilities: &[]insightapi.PackageVulnerability{
@ -81,12 +89,16 @@ var events []analyzer.AnalyzerEvent = []analyzer.AnalyzerEvent{
CheckType: checks.CheckType_CheckTypePopularity,
},
Manifest: &models.PackageManifest{
DisplayPath: "displayPath3",
Source: models.PackageManifestSource{
DisplayPath: "displayPath2",
},
},
Package: &models.Package{
PackageDetails: models.NewPackageDetail("ecosystem3", "name3", "version3"),
Manifest: &models.PackageManifest{
DisplayPath: "displayPath3",
Source: models.PackageManifestSource{
DisplayPath: "displayPath3",
},
},
Insights: &insightapi.PackageVersionInsight{
Projects: &[]insightapi.PackageProjectInfo{

View File

@ -2,21 +2,23 @@ package reporter
import (
"context"
"errors"
"fmt"
"math"
"net/http"
"strings"
"sync"
api_errors "github.com/safedep/dry/errors"
"buf.build/gen/go/safedep/api/grpc/go/safedep/services/controltower/v1/controltowerv1grpc"
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
policyv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/policy/v1"
vulnerabilityv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/vulnerability/v1"
controltowerv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/services/controltower/v1"
"github.com/safedep/dry/utils"
"github.com/safedep/vet/gen/syncv1"
"github.com/safedep/vet/internal/auth"
"github.com/safedep/vet/gen/checks"
"github.com/safedep/vet/pkg/analyzer"
"github.com/safedep/vet/pkg/common/logger"
"github.com/safedep/vet/pkg/models"
"github.com/safedep/vet/pkg/policy"
"github.com/safedep/vet/pkg/readers"
"google.golang.org/grpc"
)
const (
@ -26,84 +28,164 @@ const (
)
type SyncReporterConfig struct {
// gRPC connection for ControlTower
ClientConnection *grpc.ClientConn
// Enable multi-project syncing
// In this case, a new project is created per package manifest
EnableMultiProjectSync bool
// Required
ProjectName string
StreamName string
TriggerEvent string
ProjectName string
ProjectVersion string
TriggerEvent string
// Optional or auto-discovered from environment
ProjectSource string
GitRef string
GitRefName string
GitRefType string
GitSha string
GitRef string
GitRefName string
GitRefType string
GitSha string
// Performance
WorkerCount int
// Internal config
toolName string
toolVersion string
toolType string
// Tool details
ToolName string
ToolVersion string
}
type syncIssueWrapper struct {
retries int
issue any
type syncSession struct {
sessionId string
toolServiceClient controltowerv1grpc.ToolServiceClient
}
type syncSessionPool struct {
mu sync.RWMutex
syncSessions map[string]syncSession
}
// Only use this session
func (s *syncSessionPool) addPrimarySession(sessionId string, client controltowerv1grpc.ToolServiceClient) {
s.mu.Lock()
defer s.mu.Unlock()
s.syncSessions["*"] = syncSession{
sessionId: sessionId,
toolServiceClient: client,
}
}
func (s *syncSessionPool) hasKeyedSession(key string) bool {
s.mu.RLock()
defer s.mu.RUnlock()
_, ok := s.syncSessions[key]
return ok
}
func (s *syncSessionPool) addKeyedSession(key, sessionId string, client controltowerv1grpc.ToolServiceClient) {
s.mu.Lock()
defer s.mu.Unlock()
s.syncSessions[key] = syncSession{
sessionId: sessionId,
toolServiceClient: client,
}
}
func (s *syncSessionPool) getSession(key string) (*syncSession, error) {
s.mu.RLock()
defer s.mu.RUnlock()
if s, ok := s.syncSessions["*"]; ok {
return &s, nil
}
if s, ok := s.syncSessions[key]; ok {
return &s, nil
}
return nil, fmt.Errorf("session not found for key: %s", key)
}
func (s *syncSessionPool) forEach(f func(key string, session *syncSession) error) error {
s.mu.RLock()
defer s.mu.RUnlock()
for key, session := range s.syncSessions {
err := f(key, &session)
if err != nil {
return err
}
}
return nil
}
type workItem struct {
pkg *models.Package
event *analyzer.AnalyzerEvent
}
type syncReporter struct {
client *syncv1.ClientWithResponses
config *SyncReporterConfig
issueChannel chan *syncIssueWrapper
done chan bool
wg sync.WaitGroup
jobId string
config *SyncReporterConfig
workQueue chan *workItem
done chan bool
wg sync.WaitGroup
client *grpc.ClientConn
sessions *syncSessionPool
}
func NewSyncReporter(config SyncReporterConfig) (Reporter, error) {
apiKeyApplier := func(ctx context.Context, req *http.Request) error {
req.Header.Set("Authorization", auth.ApiKey())
return nil
if config.ClientConnection == nil {
return nil, fmt.Errorf("missing gRPC client connection")
}
// TODO: Use hysterix as the API client with retries, backoff
// connection pooling etc.
// TODO: Auto-discover config using CI environment variables
// if enabled by the user
client, err := syncv1.NewClientWithResponses(auth.DefaultSyncApiUrl(),
syncv1.WithRequestEditorFn(apiKeyApplier))
if err != nil {
return nil, fmt.Errorf("failed to initialize client for sync reporter: %w", err)
syncSessionPool := syncSessionPool{
syncSessions: make(map[string]syncSession),
}
config.TriggerEvent = string(syncv1.CreateJobRequestTriggerEventManual)
config.ProjectSource = string(syncv1.CreateJobRequestProjectSourceOther)
config.toolType = string(syncv1.CreateJobRequestToolTypeOssVet)
config.toolName = syncReporterToolName
config.toolVersion = "FIXME"
trigger := controltowerv1.ToolTrigger_TOOL_TRIGGER_MANUAL
source := packagev1.ProjectSourceType_PROJECT_SOURCE_TYPE_UNSPECIFIED
// TODO: Use an interface to auto-discover environmental details
// if not provided and update config
// A multi-project sync is required for cases like GitHub org where
// we are scanning multiple repositories
if !config.EnableMultiProjectSync {
logger.Debugf("Report Sync: Creating tool session for project: %s, version: %s",
config.ProjectName, config.ProjectVersion)
err = validateSyncReporterConfig(&config)
if err != nil {
return nil, fmt.Errorf("failed to validate sync reporter config : %w", err)
}
// Refactor this into a common session creator function
toolServiceClient := controltowerv1grpc.NewToolServiceClient(config.ClientConnection)
toolSessionRes, err := toolServiceClient.CreateToolSession(context.Background(),
&controltowerv1.CreateToolSessionRequest{
ToolName: config.ToolName,
ToolVersion: config.ToolVersion,
ProjectName: config.ProjectName,
ProjectVersion: &config.ProjectVersion,
ProjectSource: &source,
Trigger: &trigger,
})
if err != nil {
return nil, fmt.Errorf("failed to create tool session: %w", err)
}
jobId, err := createJobForSyncReportSession(client, &config)
if err != nil {
return nil, fmt.Errorf("failed to create job for sync reporter: %w", err)
logger.Debugf("Report Sync: Tool data upload session ID: %s",
toolSessionRes.GetToolSession().GetToolSessionId())
syncSessionPool.addPrimarySession(toolSessionRes.GetToolSession().GetToolSessionId(),
toolServiceClient)
}
done := make(chan bool)
issuesChan := make(chan *syncIssueWrapper, 100000)
self := &syncReporter{
config: &config,
client: client,
issueChannel: issuesChan,
jobId: jobId,
done: done,
config: &config,
done: done,
workQueue: make(chan *workItem, 1000),
client: config.ClientConnection,
sessions: &syncSessionPool,
}
self.startWorkers()
@ -115,20 +197,50 @@ func (s *syncReporter) Name() string {
}
func (s *syncReporter) AddManifest(manifest *models.PackageManifest) {
manifestSessionKey := manifest.Path
if s.config.EnableMultiProjectSync && !s.sessions.hasKeyedSession(manifestSessionKey) {
projectName := manifest.GetSource().GetNamespace()
projectVersion := "main"
source := packagev1.ProjectSourceType_PROJECT_SOURCE_TYPE_UNSPECIFIED
trigger := controltowerv1.ToolTrigger_TOOL_TRIGGER_MANUAL
logger.Debugf("Report Sync: Creating tool session for project: %s, version: %s",
projectName, projectVersion)
// Refactor this into a common session creator function
toolServiceClient := controltowerv1grpc.NewToolServiceClient(s.client)
toolSessionRes, err := toolServiceClient.CreateToolSession(context.Background(),
&controltowerv1.CreateToolSessionRequest{
ToolName: s.config.ToolName,
ToolVersion: s.config.ToolVersion,
ProjectName: projectName,
ProjectVersion: &projectVersion,
ProjectSource: &source,
Trigger: &trigger,
})
if err != nil {
logger.Errorf("failed to create tool session for project: %s/%s: %v",
projectName, projectVersion, err)
}
logger.Debugf("Report Sync: Tool data upload session ID: %s",
toolSessionRes.GetToolSession().GetToolSessionId())
s.sessions.addKeyedSession(manifestSessionKey,
toolSessionRes.GetToolSession().GetToolSessionId(), toolServiceClient)
}
// We are ignoring the error here because we are asynchronously handling the sync of Manifest
_ = readers.NewManifestModelReader(manifest).EnumPackages(func(pkg *models.Package) error {
s.queuePackageDependencyIssue(manifest, pkg)
s.queuePackageMetadataIssue(manifest, pkg)
s.queuePackageLicenseIssue(manifest, pkg)
s.queuePackageVulnerabilityIssue(manifest, pkg)
s.queuePackageScorecardIssue(manifest, pkg)
s.queuePackage(pkg)
return nil
})
}
func (s *syncReporter) AddAnalyzerEvent(event *analyzer.AnalyzerEvent) {
s.queueEvent(event)
}
func (s *syncReporter) AddPolicyEvent(event *policy.PolicyEvent) {
@ -138,12 +250,30 @@ func (s *syncReporter) Finish() error {
s.wg.Wait()
close(s.done)
return nil
return s.sessions.forEach(func(_ string, session *syncSession) error {
logger.Debugf("Report Sync: Completing tool session: %s", session.sessionId)
_, err := session.toolServiceClient.CompleteToolSession(context.Background(),
&controltowerv1.CompleteToolSessionRequest{
ToolSession: &controltowerv1.ToolSession{
ToolSessionId: session.sessionId,
},
Status: controltowerv1.CompleteToolSessionRequest_STATUS_SUCCESS,
})
return err
})
}
func (s *syncReporter) queueIssueForSync(issue *syncIssueWrapper) {
func (s *syncReporter) queueEvent(event *analyzer.AnalyzerEvent) {
s.wg.Add(1)
s.issueChannel <- issue
s.workQueue <- &workItem{event: event}
}
func (s *syncReporter) queuePackage(pkg *models.Package) {
s.wg.Add(1)
s.workQueue <- &workItem{pkg: pkg}
}
func (s *syncReporter) startWorkers() {
@ -160,10 +290,17 @@ func (s *syncReporter) startWorkers() {
func (s *syncReporter) syncReportWorker() {
for {
select {
case issue := <-s.issueChannel:
err := s.syncReportIssue(issue)
if err != nil {
logger.Errorf("failed to sync issue: %v", err)
case item := <-s.workQueue:
if item.event != nil {
err := s.syncEvent(item.event)
if err != nil {
logger.Errorf("failed to sync event: %v", err)
}
} else if item.pkg != nil {
err := s.syncPackage(item.pkg)
if err != nil {
logger.Errorf("failed to sync package: %v", err)
}
}
case <-s.done:
return
@ -171,270 +308,212 @@ func (s *syncReporter) syncReportWorker() {
}
}
func (s *syncReporter) syncReportIssue(iw *syncIssueWrapper) error {
func (s *syncReporter) syncEvent(event *analyzer.AnalyzerEvent) error {
defer s.wg.Done()
res, err := s.client.CreateJobIssueWithResponse(context.Background(), s.jobId, iw.issue)
pkg := event.Package
filter := event.Filter
if pkg == nil || filter == nil || pkg.Manifest == nil {
return fmt.Errorf("failed to sync event: invalid event data")
}
manifestSessionKey := pkg.Manifest.Path
session, err := s.sessions.getSession(manifestSessionKey)
if err != nil {
return err
return fmt.Errorf("failed to get session for package: %s/%s/%s: %w",
pkg.Manifest.Ecosystem, pkg.GetName(), pkg.GetVersion(), err)
}
defer res.HTTPResponse.Body.Close()
if res.HTTPResponse.StatusCode != http.StatusCreated {
apiErr, _ := api_errors.UnmarshalApiError(res.Body)
if apiErr.Retriable() && (iw.retries < syncReporterMaxRetries) {
iw.retries++
s.queueIssueForSync(iw)
return nil
}
return fmt.Errorf("invalid response code %d from Issue API : %w",
res.HTTPResponse.StatusCode, err)
checkType := policyv1.RuleCheck_RULE_CHECK_UNSPECIFIED
switch filter.GetCheckType() {
case checks.CheckType_CheckTypeVulnerability:
checkType = policyv1.RuleCheck_RULE_CHECK_VULNERABILITY
case checks.CheckType_CheckTypeLicense:
checkType = policyv1.RuleCheck_RULE_CHECK_LICENSE
case checks.CheckType_CheckTypeMalware:
checkType = policyv1.RuleCheck_RULE_CHECK_MALWARE
case checks.CheckType_CheckTypeMaintenance:
checkType = policyv1.RuleCheck_RULE_CHECK_MAINTENANCE
case checks.CheckType_CheckTypePopularity:
checkType = policyv1.RuleCheck_RULE_CHECK_POPULARITY
case checks.CheckType_CheckTypeSecurityScorecard:
checkType = policyv1.RuleCheck_RULE_CHECK_PROJECT_SCORECARD
default:
logger.Warnf("unsupported check type: %s", filter.GetCheckType())
}
response := utils.SafelyGetValue(res.JSON201)
logger.Debugf("Synced issued with ID: %s", response.Id)
namespace := pkg.Manifest.GetSource().GetNamespace()
req := controltowerv1.PublishPolicyViolationRequest{
ToolSession: &controltowerv1.ToolSession{
ToolSessionId: session.sessionId,
},
Manifest: &packagev1.PackageManifest{
Ecosystem: pkg.Manifest.GetControlTowerSpecEcosystem(),
Namespace: &namespace,
Name: pkg.Manifest.GetSource().GetPath(),
},
PackageVersion: &packagev1.PackageVersion{
Package: &packagev1.Package{
Ecosystem: pkg.Manifest.GetControlTowerSpecEcosystem(),
Name: pkg.Name,
},
Version: pkg.Version,
},
Violation: &policyv1.Violation{
Rule: &policyv1.Rule{
Name: filter.GetName(),
Description: filter.GetSummary(),
Value: filter.GetValue(),
Check: checkType,
},
Evidences: []*policyv1.ViolationEvidence{},
},
}
_, err = session.toolServiceClient.PublishPolicyViolation(context.Background(), &req)
if err != nil {
return fmt.Errorf("failed to publish policy violation: %w", err)
}
return nil
}
func (s *syncReporter) buildPackageIssue(issueType syncv1.IssueIssueType,
manifest *models.PackageManifest, pkg *models.Package) syncv1.PackageIssue {
ecosystem := syncv1.PackageIssueManifestEcosystem(manifest.Ecosystem)
return syncv1.PackageIssue{
Issue: syncv1.Issue{
IssueType: issueType,
func (s *syncReporter) syncPackage(pkg *models.Package) error {
defer s.wg.Done()
manifestSessionKey := pkg.Manifest.Path
session, err := s.sessions.getSession(manifestSessionKey)
if err != nil {
return fmt.Errorf("failed to get session for package: %s/%s/%s: %w",
pkg.Manifest.Ecosystem, pkg.GetName(), pkg.GetVersion(), err)
}
// Build the base package manifest and package
req := controltowerv1.PublishPackageInsightRequest{
ToolSession: &controltowerv1.ToolSession{
ToolSessionId: session.sessionId,
},
ManifestEcosystem: &ecosystem,
ManifestPath: &manifest.Path,
PackageName: &pkg.Name,
PackageVersion: &pkg.Version,
}
}
func (s *syncReporter) queuePackageDependencyIssue(manifest *models.PackageManifest, pkg *models.Package) {
issue := syncv1.IssuePackageDependency{
PackageIssue: s.buildPackageIssue(syncv1.IssueIssueTypePackageDependency,
manifest, pkg),
Manifest: &packagev1.PackageManifest{
Ecosystem: pkg.Manifest.GetControlTowerSpecEcosystem(),
Namespace: &pkg.Manifest.Path,
Name: pkg.Manifest.GetDisplayPath(),
},
PackageVersion: &packagev1.PackageVersion{
Package: &packagev1.Package{
Ecosystem: pkg.Manifest.GetControlTowerSpecEcosystem(),
Name: pkg.Name,
},
Version: pkg.Version,
},
PackageVersionInsight: &packagev1.PackageVersionInsight{
Dependencies: []*packagev1.PackageVersion{},
Vulnerabilities: []*vulnerabilityv1.Vulnerability{},
ProjectInsights: []*packagev1.ProjectInsight{},
Licenses: &packagev1.LicenseMetaList{
Licenses: []*packagev1.LicenseMeta{},
},
},
}
if pkg.Parent != nil {
issue.ParentPackageName = &pkg.Parent.Name
issue.ParentPackageVersion = &pkg.Parent.Version
}
// Add package dependencies
dependencies, err := pkg.GetDependencies()
if err != nil {
logger.Warnf("failed to get dependencies for package: %s/%s/%s: %s",
pkg.Manifest.Ecosystem, pkg.GetName(), pkg.GetVersion(), err.Error())
} else {
for _, child := range dependencies {
req.PackageVersionInsight.Dependencies = append(req.PackageVersionInsight.Dependencies, &packagev1.PackageVersion{
Package: &packagev1.Package{
Ecosystem: child.Manifest.GetControlTowerSpecEcosystem(),
Name: child.GetName(),
},
iw := syncIssueWrapper{issue: issue}
s.queueIssueForSync(&iw)
}
func (s *syncReporter) queuePackageMetadataIssue(manifest *models.PackageManifest, pkg *models.Package) {
insights := utils.SafelyGetValue(pkg.Insights)
projects := utils.SafelyGetValue(insights.Projects)
for _, project := range projects {
issue := syncv1.IssuePackageSource{
PackageIssue: s.buildPackageIssue(syncv1.IssueIssueTypePackageSourceInfo,
manifest, pkg),
}
sourceType := utils.SafelyGetValue(project.Type)
issueSourceType := syncv1.IssuePackageSourceSourceTypeOther
switch sourceType {
case "GITHUB":
issueSourceType = syncv1.IssuePackageSourceSourceTypeGithub
case "BITBUCKET":
issueSourceType = syncv1.IssuePackageSourceSourceTypeBitbucket
case "GITLAB":
issueSourceType = syncv1.IssuePackageSourceSourceTypeGitlab
}
issue.SourceType = &issueSourceType
issue.SourceUrl = project.Link
issue.SourceDisplayName = project.DisplayName
issue.SourceForks = project.Forks
issue.SourceStars = project.Stars
s.queueIssueForSync(&syncIssueWrapper{issue: issue})
}
}
func (s *syncReporter) queuePackageLicenseIssue(manifest *models.PackageManifest, pkg *models.Package) {
insights := utils.SafelyGetValue(pkg.Insights)
licenses := utils.SafelyGetValue(insights.Licenses)
for _, license := range licenses {
licenseId := syncv1.IssuePackageLicenseLicenseId(license)
issue := syncv1.IssuePackageLicense{
PackageIssue: s.buildPackageIssue(syncv1.IssueIssueTypePackageLicense,
manifest, pkg),
LicenseId: &licenseId,
}
s.queueIssueForSync(&syncIssueWrapper{issue: issue})
}
}
func (s *syncReporter) queuePackageVulnerabilityIssue(manifest *models.PackageManifest, pkg *models.Package) {
insights := utils.SafelyGetValue(pkg.Insights)
vulnerabilities := utils.SafelyGetValue(insights.Vulnerabilities)
for _, vuln := range vulnerabilities {
issue := syncv1.IssuePackageVulnerability{
PackageIssue: s.buildPackageIssue(syncv1.IssueIssueTypePackageVulnerability,
manifest, pkg),
}
severities := []struct {
Risk *syncv1.IssuePackageCommonVulnerabilitySeveritiesRisk `json:"risk,omitempty"`
Score *string `json:"score,omitempty"`
Type *syncv1.IssuePackageCommonVulnerabilitySeveritiesType `json:"type,omitempty"`
}{}
issue.Vulnerability = &syncv1.IssuePackageCommonVulnerability{
Id: vuln.Id,
Summary: vuln.Summary,
Aliases: vuln.Aliases,
Related: vuln.Related,
}
insightsVulnSeverities := utils.SafelyGetValue(vuln.Severities)
for _, severity := range insightsVulnSeverities {
sRisk := syncv1.IssuePackageCommonVulnerabilitySeveritiesRisk(utils.SafelyGetValue(severity.Risk))
sType := syncv1.IssuePackageCommonVulnerabilitySeveritiesType(utils.SafelyGetValue(severity.Type))
severities = append(severities, struct {
Risk *syncv1.IssuePackageCommonVulnerabilitySeveritiesRisk `json:"risk,omitempty"`
Score *string `json:"score,omitempty"`
Type *syncv1.IssuePackageCommonVulnerabilitySeveritiesType `json:"type,omitempty"`
}{
Score: severity.Score,
Risk: &sRisk,
Type: &sType,
Version: child.GetVersion(),
})
}
issue.Vulnerability.Severities = &severities
s.queueIssueForSync(&syncIssueWrapper{issue: issue})
}
}
func (s *syncReporter) queuePackageScorecardIssue(manifest *models.PackageManifest, pkg *models.Package) {
// Get the insights
insights := utils.SafelyGetValue(pkg.Insights)
// Basic sanity test to fail fast if Scorecard is unavailable
if (insights.Scorecard == nil) || (insights.Scorecard.Content == nil) {
return
// Add vulnerabilities. We will publish only the minimum required information.
// The backend should have its own VDB to enrich the data.
vulnerabilities := utils.SafelyGetValue(insights.Vulnerabilities)
for _, v := range vulnerabilities {
vId := utils.SafelyGetValue(v.Id)
vulnerability := vulnerabilityv1.Vulnerability{
Id: &vulnerabilityv1.VulnerabilityIdentifier{
Value: vId,
},
Summary: utils.SafelyGetValue(v.Summary),
}
if strings.HasPrefix(vId, "CVE-") {
vulnerability.Id.Type = vulnerabilityv1.VulnerabilityIdentifierType_VULNERABILITY_IDENTIFIER_TYPE_CVE
} else if strings.HasPrefix(vId, "OSV-") {
vulnerability.Id.Type = vulnerabilityv1.VulnerabilityIdentifierType_VULNERABILITY_IDENTIFIER_TYPE_OSV
}
req.PackageVersionInsight.Vulnerabilities = append(req.PackageVersionInsight.Vulnerabilities, &vulnerability)
}
scorecard := utils.SafelyGetValue(insights.Scorecard)
scorecardContent := utils.SafelyGetValue(scorecard.Content)
scorecardRepo := utils.SafelyGetValue(scorecardContent.Repository)
scorecardChecks := utils.SafelyGetValue(scorecardContent.Checks)
// Add project information
project := utils.SafelyGetValue(insights.Projects)
for _, p := range project {
stars := int64(utils.SafelyGetValue(p.Stars))
forks := int64(utils.SafelyGetValue(p.Forks))
issues := int64(utils.SafelyGetValue(p.Issues))
date := utils.SafelyGetValue(scorecardContent.Date).Format("2006-01-02")
repo := struct {
Commit *string `json:"commit,omitempty"`
Name *string `json:"name,omitempty"`
}{
Commit: scorecardRepo.Commit,
Name: scorecardRepo.Name,
}
vt := packagev1.ProjectSourceType_PROJECT_SOURCE_TYPE_UNSPECIFIED
switch utils.SafelyGetValue(p.Type) {
case "GITHUB":
vt = packagev1.ProjectSourceType_PROJECT_SOURCE_TYPE_GITHUB
case "GITLAB":
vt = packagev1.ProjectSourceType_PROJECT_SOURCE_TYPE_GITLAB
}
checks := []struct {
Details *[]string `json:"details,omitempty"`
Documentation *struct {
Short *string `json:"short,omitempty"`
Url *string `json:"url,omitempty"`
} `json:"documentation,omitempty"`
Name *string `json:"name,omitempty"`
Reason *string `json:"reason,omitempty"`
Score *int `json:"score"`
}{}
req.PackageVersionInsight.ProjectInsights = append(req.PackageVersionInsight.ProjectInsights, &packagev1.ProjectInsight{
Project: &packagev1.Project{
Type: vt,
Name: utils.SafelyGetValue(p.Name),
Url: utils.SafelyGetValue(p.Link),
},
for _, check := range scorecardChecks {
// We have to do this stupid type conversion because Scorecard check score seems
// be to int / float32 in different specs. We are good with downsizing width because
// scorecard score is max 10
checkScore := utils.SafelyGetValue(check.Score)
checkScoreInt := int(math.Round(float64(checkScore)))
checks = append(checks, struct {
Details *[]string `json:"details,omitempty"`
Documentation *struct {
Short *string `json:"short,omitempty"`
Url *string `json:"url,omitempty"`
} `json:"documentation,omitempty"`
Name *string `json:"name,omitempty"`
Reason *string `json:"reason,omitempty"`
Score *int `json:"score"`
}{
Details: check.Details,
Name: (*string)(check.Name),
Reason: check.Reason,
Score: &checkScoreInt,
Stars: &stars,
Forks: &forks,
Issues: &packagev1.ProjectInsight_IssueStat{
Total: issues,
},
})
}
issue := &syncv1.IssuePackageScorecard{
PackageIssue: s.buildPackageIssue(syncv1.IssueIssueTypePackageOpenssfScorecard,
manifest, pkg),
Date: &date,
Score: scorecardContent.Score,
Repo: &repo,
Checks: &checks,
licenses := utils.SafelyGetValue(insights.Licenses)
for _, license := range licenses {
req.PackageVersionInsight.Licenses.Licenses = append(req.PackageVersionInsight.Licenses.Licenses, &packagev1.LicenseMeta{
LicenseId: string(license),
Name: string(license),
})
}
s.queueIssueForSync(&syncIssueWrapper{issue: issue})
}
// OpenSSF
// We can't use vet's collected scorecard because its data model is wrong. There is
// not a single scorecard per package. Rather there is a scorecard per project. Since
// a package may be related to multiple projects, we will have multiple related scorecards.
func validateSyncReporterConfig(config *SyncReporterConfig) error {
if utils.IsEmptyString(config.ProjectName) {
return errors.New("project name not in config")
}
if utils.IsEmptyString(config.StreamName) {
return errors.New("stream name not in config")
}
if utils.IsEmptyString(config.TriggerEvent) {
return errors.New("trigger event not in config")
_, err = session.toolServiceClient.PublishPackageInsight(context.Background(), &req)
if err != nil {
return fmt.Errorf("failed to publish package insight: %w", err)
}
return nil
}
func createJobForSyncReportSession(client *syncv1.ClientWithResponses,
config *SyncReporterConfig) (string, error) {
jobConfig := syncv1.CreateSyncJobJSONRequestBody{
ProjectName: config.ProjectName,
ProjectSource: syncv1.CreateJobRequestProjectSource(config.ProjectSource),
StreamName: config.StreamName,
TriggerEvent: syncv1.CreateJobRequestTriggerEvent(config.TriggerEvent),
ToolType: syncv1.CreateJobRequestToolType(config.toolType),
ToolName: config.toolName,
ToolVersion: config.toolVersion,
GitRefType: (*syncv1.CreateJobRequestGitRefType)(&config.GitRefType),
GitRef: &config.GitRef,
GitRefName: &config.GitRefName,
GitSha: &config.GitSha,
}
job, err := client.CreateSyncJobWithResponse(context.Background(), jobConfig)
if err != nil {
return "", err
}
defer job.HTTPResponse.Body.Close()
if job.HTTPResponse.StatusCode != http.StatusCreated {
err, _ = api_errors.UnmarshalApiError(job.Body)
return "", fmt.Errorf("invalid response code %d from Job API : %w",
job.HTTPResponse.StatusCode, err)
}
res := utils.SafelyGetValue(job.JSON201)
return utils.SafelyGetValue(res.Id), nil
}

22
scan.go
View File

@ -51,6 +51,7 @@ var (
disableAuthVerifyBeforeScan bool
syncReport bool
syncReportProject string
syncEnableMultiProject bool
graphReportDirectory string
syncReportStream string
listExperimentalParsers bool
@ -140,7 +141,9 @@ func newScanCommand() *cobra.Command {
"Enable syncing report data to cloud")
cmd.Flags().StringVarP(&syncReportProject, "report-sync-project", "", "",
"Project name to use in cloud")
cmd.Flags().StringVarP(&syncReportStream, "report-sync-stream", "", "",
cmd.Flags().BoolVarP(&syncEnableMultiProject, "report-sync-multi-project", "", false,
"Lazily create cloud sessions for multiple projects (per manifest)")
cmd.Flags().StringVarP(&syncReportStream, "report-sync-project-version", "", "",
"Project stream name (e.g. branch) to use in cloud")
cmd.Flags().StringArrayVarP(&trustedRegistryUrls, "trusted-registry", "", []string{},
"Trusted registry URLs to use for package manifest verification")
@ -175,9 +178,7 @@ func listParsersCommand() *cobra.Command {
func startScan() {
if !disableAuthVerifyBeforeScan {
err := auth.Verify(&auth.VerifyConfig{
ControlPlaneApiUrl: auth.DefaultControlPlaneApiUrl(),
})
err := auth.Verify()
// We will fallback to community mode by default to provide
// a seamless user experience
@ -393,9 +394,18 @@ func internalStartScan() error {
}
if syncReport {
clientConn, err := auth.SyncClientConnection("vet-sync")
if err != nil {
return err
}
rp, err := reporter.NewSyncReporter(reporter.SyncReporterConfig{
ProjectName: syncReportProject,
StreamName: syncReportStream,
ToolName: "vet",
ToolVersion: version,
ProjectName: syncReportProject,
ProjectVersion: syncReportStream,
EnableMultiProjectSync: syncEnableMultiProject,
ClientConnection: clientConn,
})
if err != nil {
return err

View File

@ -7,8 +7,6 @@ export E2E_ROOT="$E2E_THIS_DIR/../../"
export E2E_FIXTURES="$E2E_THIS_DIR/fixtures"
export E2E_VET_BINARY="$E2E_ROOT/vet"
$E2E_VET_BINARY auth configure --community
bash $E2E_THIS_DIR/scenario-1-vet-scans-vet.sh
bash $E2E_THIS_DIR/scenario-2-vet-scan-demo-client-java.sh
bash $E2E_THIS_DIR/scenario-3-filter-fail-fast.sh