Merge pull request #30 from safedep/develop

#7: Add support for auth verify command
This commit is contained in:
Abhisek Datta 2023-03-16 15:47:10 +05:30 committed by GitHub
commit ab3d7e9f34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 664 additions and 41 deletions

View File

@ -13,8 +13,10 @@ protoc-install:
oapi-codegen: oapi-codegen:
oapi-codegen -package insightapi -generate types ./api/insights-v1.yml > ./gen/insightapi/insights.types.go 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 insightapi -generate client ./api/insights-v1.yml > ./gen/insightapi/insights.client.go
oapi-codegen -package controlplane -generate types ./api/cp-v1-trials.yml > ./gen/controlplane/trials.types.go oapi-codegen -package cpv1trials -generate types ./api/cp-v1-trials.yml > ./gen/cpv1trials/trials.types.go
oapi-codegen -package controlplane -generate client ./api/cp-v1-trials.yml > ./gen/controlplane/trials.client.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
protoc-codegen: protoc-codegen:
protoc -I ./api \ protoc -I ./api \
@ -33,7 +35,8 @@ protoc-codegen:
setup: setup:
mkdir -p out \ mkdir -p out \
gen/insightapi \ gen/insightapi \
gen/controlplane \ gen/cpv1trials \
gen/cpv1 \
gen/filterinput \ gen/filterinput \
gen/filtersuite \ gen/filtersuite \
gen/exceptionsapi gen/exceptionsapi

View File

@ -55,9 +55,16 @@ vet auth configure
``` ```
> Insights API is used to enrich OSS packages with meta-data for rich query and policy > Insights API is used to enrich OSS packages with meta-data for rich query and policy
> decisions > decisions. Alternatively, the API key can be passed through environment
> variable `VET_API_KEY`
Run `vet` to identify risks Verify authentication token is valid
```bash
vet auth verify
```
Run `vet` to identify risks in a source repository
```bash ```bash
vet scan -D /path/to/repository vet scan -D /path/to/repository

113
api/cp-v1.yml Normal file
View File

@ -0,0 +1,113 @@
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

18
auth.go
View File

@ -10,6 +10,7 @@ import (
"golang.org/x/term" "golang.org/x/term"
"github.com/safedep/vet/internal/auth" "github.com/safedep/vet/internal/auth"
"github.com/safedep/vet/internal/ui"
) )
var ( var (
@ -27,6 +28,9 @@ func newAuthCommand() *cobra.Command {
}, },
} }
cmd.PersistentFlags().StringVarP(&authControlPlaneApiBaseUrl, "control-plane", "",
auth.DefaultControlPlaneApiUrl(), "Base URL of Control Plane API")
cmd.AddCommand(configureAuthCommand()) cmd.AddCommand(configureAuthCommand())
cmd.AddCommand(verifyAuthCommand()) cmd.AddCommand(verifyAuthCommand())
cmd.AddCommand(trialsRegisterCommand()) cmd.AddCommand(trialsRegisterCommand())
@ -45,8 +49,9 @@ func configureAuthCommand() *cobra.Command {
} }
err = auth.Configure(auth.Config{ err = auth.Configure(auth.Config{
ApiUrl: authInsightApiBaseUrl, ApiUrl: authInsightApiBaseUrl,
ApiKey: string(key), ApiKey: string(key),
ControlPlaneApiUrl: authControlPlaneApiBaseUrl,
}) })
if err != nil { if err != nil {
@ -69,8 +74,11 @@ func verifyAuthCommand() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "verify", Use: "verify",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
fmt.Printf("Verify auth command is currently work in progress\n") failOnError("auth/verify", auth.Verify(&auth.VerifyConfig{
os.Exit(1) ControlPlaneApiUrl: authControlPlaneApiBaseUrl,
}))
ui.PrintSuccess("Authentication key is valid!")
return nil return nil
}, },
} }
@ -103,8 +111,6 @@ func trialsRegisterCommand() *cobra.Command {
cmd.Flags().StringVarP(&authTrialEmail, "email", "", "", cmd.Flags().StringVarP(&authTrialEmail, "email", "", "",
"Email address to use for sending trial API key") "Email address to use for sending trial API key")
cmd.Flags().StringVarP(&authControlPlaneApiBaseUrl, "control-plane", "",
auth.DefaultControlPlaneApiUrl(), "Base URL of Control Plane API for registrations")
return cmd return cmd
} }

258
gen/cpv1/cp.client.go Normal file
View File

@ -0,0 +1,258 @@
// Package cpv1 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 cpv1
import (
"context"
"encoding/json"
"fmt"
"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 {
// GetApiCredentialIntrospection request
GetApiCredentialIntrospection(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
}
func (c *Client) GetApiCredentialIntrospection(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewGetApiCredentialIntrospectionRequest(c.Server)
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)
}
// NewGetApiCredentialIntrospectionRequest generates requests for GetApiCredentialIntrospection
func NewGetApiCredentialIntrospectionRequest(server string) (*http.Request, error) {
var err error
serverURL, err := url.Parse(server)
if err != nil {
return nil, err
}
operationPath := fmt.Sprintf("/auths/me")
if operationPath[0] == '/' {
operationPath = "." + operationPath
}
queryURL, err := serverURL.Parse(operationPath)
if err != nil {
return nil, err
}
req, err := http.NewRequest("GET", queryURL.String(), nil)
if err != nil {
return nil, err
}
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 {
// GetApiCredentialIntrospection request
GetApiCredentialIntrospectionWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetApiCredentialIntrospectionResponse, error)
}
type GetApiCredentialIntrospectionResponse struct {
Body []byte
HTTPResponse *http.Response
JSON200 *CredentialIntrospectionResponse
JSON403 *ApiError
JSON429 *ApiError
JSON500 *ApiError
}
// Status returns HTTPResponse.Status
func (r GetApiCredentialIntrospectionResponse) Status() string {
if r.HTTPResponse != nil {
return r.HTTPResponse.Status
}
return http.StatusText(0)
}
// StatusCode returns HTTPResponse.StatusCode
func (r GetApiCredentialIntrospectionResponse) StatusCode() int {
if r.HTTPResponse != nil {
return r.HTTPResponse.StatusCode
}
return 0
}
// GetApiCredentialIntrospectionWithResponse request returning *GetApiCredentialIntrospectionResponse
func (c *ClientWithResponses) GetApiCredentialIntrospectionWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetApiCredentialIntrospectionResponse, error) {
rsp, err := c.GetApiCredentialIntrospection(ctx, reqEditors...)
if err != nil {
return nil, err
}
return ParseGetApiCredentialIntrospectionResponse(rsp)
}
// ParseGetApiCredentialIntrospectionResponse parses an HTTP response from a GetApiCredentialIntrospectionWithResponse call
func ParseGetApiCredentialIntrospectionResponse(rsp *http.Response) (*GetApiCredentialIntrospectionResponse, error) {
bodyBytes, err := ioutil.ReadAll(rsp.Body)
defer func() { _ = rsp.Body.Close() }()
if err != nil {
return nil, err
}
response := &GetApiCredentialIntrospectionResponse{
Body: bodyBytes,
HTTPResponse: rsp,
}
switch {
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
var dest CredentialIntrospectionResponse
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
}

159
gen/cpv1/cp.types.go Normal file
View File

@ -0,0 +1,159 @@
// Package cpv1 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 cpv1
import (
"encoding/json"
"fmt"
)
// 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"
)
// Defines values for CredentialIntrospectionResponseType.
const (
CredentialIntrospectionResponseTypeOrganization CredentialIntrospectionResponseType = "Organization"
CredentialIntrospectionResponseTypeTeam CredentialIntrospectionResponseType = "Team"
CredentialIntrospectionResponseTypeTrialUser CredentialIntrospectionResponseType = "TrialUser"
CredentialIntrospectionResponseTypeUser CredentialIntrospectionResponseType = "User"
)
// 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
// CredentialIntrospectionResponse defines model for CredentialIntrospectionResponse.
type CredentialIntrospectionResponse struct {
// Expiry timestamp in RFC3399 format if expirable
Expiry *string `json:"expiry,omitempty"`
// The entity type to which this credential belongs to
Type CredentialIntrospectionResponseType `json:"type"`
}
// The entity type to which this credential belongs to
type CredentialIntrospectionResponseType string
// 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,7 +1,7 @@
// Package controlplane provides primitives to interact with the openapi HTTP API. // 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. // Code generated by github.com/deepmap/oapi-codegen version v1.10.1 DO NOT EDIT.
package controlplane package cpv1trials
import ( import (
"bytes" "bytes"

View File

@ -1,7 +1,7 @@
// Package controlplane provides primitives to interact with the openapi HTTP API. // 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. // Code generated by github.com/deepmap/oapi-codegen version v1.10.1 DO NOT EDIT.
package controlplane package cpv1trials
import ( import (
"encoding/json" "encoding/json"

View File

@ -10,8 +10,9 @@ import (
) )
const ( const (
apiUrlEnvKey = "VET_INSIGHTS_API_URL" apiUrlEnvKey = "VET_INSIGHTS_API_URL"
apiKeyEnvKey = "VET_INSIGHTS_API_KEY" apiKeyEnvKey = "VET_INSIGHTS_API_KEY"
apiKeyAlternateEnvKey = "VET_API_KEY"
defaultApiUrl = "https://api.safedep.io/insights/v1" defaultApiUrl = "https://api.safedep.io/insights/v1"
defaultControlPlaneApiUrl = "https://api.safedep.io/control-plane/v1" defaultControlPlaneApiUrl = "https://api.safedep.io/control-plane/v1"
@ -20,8 +21,9 @@ const (
) )
type Config struct { type Config struct {
ApiUrl string `yaml:"api_url"` ApiUrl string `yaml:"api_url"`
ApiKey string `yaml:"api_key"` ApiKey string `yaml:"api_key"`
ControlPlaneApiUrl string `yaml:"cp_api_url"`
} }
// Global config to be used during runtime // Global config to be used during runtime
@ -36,16 +38,15 @@ func Configure(m Config) error {
return persistConfiguration() return persistConfiguration()
} }
func Verify() error {
// TODO: Verify by actually calling insight API
return nil
}
func DefaultApiUrl() string { func DefaultApiUrl() string {
return defaultApiUrl return defaultApiUrl
} }
func DefaultControlPlaneApiUrl() string { func DefaultControlPlaneApiUrl() string {
if (globalConfig != nil) && (globalConfig.ControlPlaneApiUrl != "") {
return globalConfig.ControlPlaneApiUrl
}
return defaultControlPlaneApiUrl return defaultControlPlaneApiUrl
} }
@ -66,6 +67,10 @@ func ApiKey() string {
return key return key
} }
if key, ok := os.LookupEnv(apiKeyAlternateEnvKey); ok {
return key
}
if globalConfig != nil { if globalConfig != nil {
return globalConfig.ApiKey return globalConfig.ApiKey
} }

View File

@ -9,7 +9,7 @@ import (
"github.com/deepmap/oapi-codegen/pkg/types" "github.com/deepmap/oapi-codegen/pkg/types"
"github.com/safedep/dry/utils" "github.com/safedep/dry/utils"
"github.com/safedep/vet/gen/controlplane" "github.com/safedep/vet/gen/cpv1trials"
"github.com/safedep/vet/pkg/common/logger" "github.com/safedep/vet/pkg/common/logger"
apierr "github.com/safedep/dry/errors" apierr "github.com/safedep/dry/errors"
@ -45,7 +45,7 @@ func (client *trialRegistrationClient) Execute() (*trialRegistrationResponse, er
logger.Infof("Trial registrations using Control Plane: %s", logger.Infof("Trial registrations using Control Plane: %s",
client.config.ControlPlaneApiUrl) client.config.ControlPlaneApiUrl)
cpClient, err := controlplane.NewClientWithResponses(client.config.ControlPlaneApiUrl) cpClient, err := cpv1trials.NewClientWithResponses(client.config.ControlPlaneApiUrl)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -54,7 +54,7 @@ func (client *trialRegistrationClient) Execute() (*trialRegistrationResponse, er
client.config.Email) client.config.Email)
res, err := cpClient.RegisterTrialUserWithResponse(context.Background(), res, err := cpClient.RegisterTrialUserWithResponse(context.Background(),
controlplane.RegisterTrialUserJSONRequestBody{ cpv1trials.RegisterTrialUserJSONRequestBody{
Email: types.Email(client.config.Email), Email: types.Email(client.config.Email),
}) })
if err != nil { if err != nil {

57
internal/auth/verify.go Normal file
View File

@ -0,0 +1,57 @@
package auth
import (
"context"
"fmt"
"net/http"
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 {
logger.Infof("Verifying auth token using Control Plane: %s", config.ControlPlaneApiUrl)
client, err := cpv1.NewClientWithResponses(config.ControlPlaneApiUrl)
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)
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)
}
}
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
}

View File

@ -11,6 +11,11 @@ func PrintBanner(s string) {
fmt.Fprintf(os.Stderr, s) fmt.Fprintf(os.Stderr, s)
} }
func PrintSuccess(s string, args ...any) {
msg := fmt.Sprintf(s, args...)
fmt.Fprint(os.Stderr, text.FgGreen.Sprint(msg), "\n")
}
func PrintError(s string, args ...any) { func PrintError(s string, args ...any) {
msg := fmt.Sprintf(s, args...) msg := fmt.Sprintf(s, args...)
fmt.Fprint(os.Stderr, text.FgRed.Sprint(msg), "\n") fmt.Fprint(os.Stderr, text.FgRed.Sprint(msg), "\n")

38
scan.go
View File

@ -5,6 +5,7 @@ import (
"os" "os"
"github.com/safedep/dry/utils" "github.com/safedep/dry/utils"
"github.com/safedep/vet/internal/auth"
"github.com/safedep/vet/internal/ui" "github.com/safedep/vet/internal/ui"
"github.com/safedep/vet/pkg/analyzer" "github.com/safedep/vet/pkg/analyzer"
"github.com/safedep/vet/pkg/models" "github.com/safedep/vet/pkg/models"
@ -15,20 +16,21 @@ import (
) )
var ( var (
lockfiles []string lockfiles []string
lockfileAs string lockfileAs string
baseDirectory string baseDirectory string
transitiveAnalysis bool transitiveAnalysis bool
transitiveDepth int transitiveDepth int
concurrency int concurrency int
dumpJsonManifestDir string dumpJsonManifestDir string
celFilterExpression string celFilterExpression string
celFilterSuiteFile string celFilterSuiteFile string
celFilterFailOnMatch bool celFilterFailOnMatch bool
markdownReportPath string markdownReportPath string
consoleReport bool consoleReport bool
summaryReport bool summaryReport bool
silentScan bool silentScan bool
disableAuthVerifyBeforeScan bool
) )
func newScanCommand() *cobra.Command { func newScanCommand() *cobra.Command {
@ -68,6 +70,8 @@ func newScanCommand() *cobra.Command {
"Filter packages using CEL Filter Suite from file") "Filter packages using CEL Filter Suite from file")
cmd.Flags().BoolVarP(&celFilterFailOnMatch, "filter-fail", "", false, cmd.Flags().BoolVarP(&celFilterFailOnMatch, "filter-fail", "", false,
"Fail the scan if the filter match any package (security gate)") "Fail the scan if the filter match any package (security gate)")
cmd.Flags().BoolVarP(&disableAuthVerifyBeforeScan, "no-verify-auth", "", false,
"Do not verify auth token before starting scan")
cmd.Flags().StringVarP(&markdownReportPath, "report-markdown", "", "", cmd.Flags().StringVarP(&markdownReportPath, "report-markdown", "", "",
"Generate consolidated markdown report to file") "Generate consolidated markdown report to file")
cmd.Flags().BoolVarP(&consoleReport, "report-console", "", false, cmd.Flags().BoolVarP(&consoleReport, "report-console", "", false,
@ -97,6 +101,12 @@ func listParsersCommand() *cobra.Command {
} }
func startScan() { func startScan() {
if !disableAuthVerifyBeforeScan {
failOnError("auth/verify", auth.Verify(&auth.VerifyConfig{
ControlPlaneApiUrl: auth.DefaultControlPlaneApiUrl(),
}))
}
failOnError("scan", internalStartScan()) failOnError("scan", internalStartScan())
} }