diff --git a/.gitignore b/.gitignore index 3bba0a68f..d0b92871e 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,4 @@ git-sensor/git-sensor kubelink/kubelink kubewatch/kubewatch lens/lens - +image-scanner/image-scanner diff --git a/Makefile b/Makefile index 3921095a0..b7c31a836 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ dep-update-oss: cd kubelink && TARGET_BRANCH=$(TARGET_BRANCH) $(MAKE) dep-update-oss cd kubewatch && TARGET_BRANCH=$(TARGET_BRANCH) $(MAKE) dep-update-oss cd lens && TARGET_BRANCH=$(TARGET_BRANCH) $(MAKE) dep-update-oss + cd image-scanner && TARGET_BRANCH=$(TARGET_BRANCH) $(MAKE) dep-update-oss build: cd chart-sync && $(MAKE) @@ -20,4 +21,5 @@ build: cd kubelink && $(MAKE) cd kubewatch && $(MAKE) cd lens && $(MAKE) + cd image-scanner && $(MAKE) # cd common-lib && $(MAKE) \ No newline at end of file diff --git a/image-scanner/App.go b/image-scanner/App.go new file mode 100644 index 000000000..b2280998d --- /dev/null +++ b/image-scanner/App.go @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "context" + "fmt" + "github.com/caarlos0/env" + "github.com/devtron-labs/image-scanner/pkg/middleware" + "net/http" + "os" + "time" + + "github.com/devtron-labs/common-lib/middlewares" + client "github.com/devtron-labs/common-lib/pubsub-lib" + "github.com/devtron-labs/image-scanner/api" + "github.com/devtron-labs/image-scanner/pubsub" + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type App struct { + Router *api.Router + Logger *zap.SugaredLogger + server *http.Server + db *pg.DB + natsSubscription *pubsub.NatSubscriptionImpl + pubSubClient *client.PubSubClientServiceImpl +} + +func NewApp(Router *api.Router, Logger *zap.SugaredLogger, + db *pg.DB, natsSubscription *pubsub.NatSubscriptionImpl, + pubSubClient *client.PubSubClientServiceImpl) *App { + return &App{ + Router: Router, + Logger: Logger, + db: db, + natsSubscription: natsSubscription, + pubSubClient: pubSubClient, + } +} + +type ServerConfig struct { + SERVER_HTTP_PORT int `env:"SERVER_HTTP_PORT" envDefault:"8080"` +} + +func (app *App) Start() { + serverConfig := ServerConfig{} + err := env.Parse(&serverConfig) + if err != nil { + app.Logger.Errorw("error in parsing server config from environment", "err", err) + os.Exit(2) + } + httpPort := serverConfig.SERVER_HTTP_PORT + app.Logger.Infow("starting server on ", "httpPort", httpPort) + app.Router.Init() + server := &http.Server{Addr: fmt.Sprintf(":%d", httpPort), Handler: app.Router.Router} + app.Router.Router.Use(middleware.PrometheusMiddleware) + app.Router.Router.Use(middlewares.Recovery) + app.server = server + err = server.ListenAndServe() + if err != nil { + app.Logger.Errorw("error in startup", "err", err) + os.Exit(2) + } +} + +func (app *App) Stop() { + app.Logger.Infow("image scanner shutdown initiating") + timeoutContext, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + app.Logger.Infow("closing router") + err := app.server.Shutdown(timeoutContext) + if err != nil { + app.Logger.Errorw("error in mux router shutdown", "err", err) + } + app.Logger.Infow("closing db connection") + err = app.db.Close() + if err != nil { + app.Logger.Errorw("Error while closing DB", "error", err) + } + + app.Logger.Infow("housekeeping done. exiting now") +} diff --git a/image-scanner/Dockerfile b/image-scanner/Dockerfile new file mode 100644 index 000000000..2d0e1fa3b --- /dev/null +++ b/image-scanner/Dockerfile @@ -0,0 +1,20 @@ +FROM golang:1.21-alpine3.18 AS build-env +RUN apk add --no-cache git gcc musl-dev +RUN apk add --update make +RUN go install github.com/google/wire/cmd/wire@latest +WORKDIR /go/src/github.com/devtron-labs/image-scanner +ADD . /go/src/github.com/devtron-labs/image-scanner +RUN GOOS=linux make + +FROM alpine:3.17 +COPY --from=aquasec/trivy:0.46.1 /usr/local/bin/trivy /usr/local/bin/trivy +RUN apk add --no-cache ca-certificates +RUN mkdir -p /security +RUN adduser -D devtron +COPY --from=build-env /go/src/github.com/devtron-labs/image-scanner/image-scanner . +RUN chown -R devtron:devtron ./image-scanner +RUN chmod +x ./image-scanner +RUN chown -R devtron:devtron ./security +RUN chmod +x ./security +USER devtron +CMD ["./image-scanner"] diff --git a/image-scanner/LICENSE b/image-scanner/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/image-scanner/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/image-scanner/Makefile b/image-scanner/Makefile new file mode 100644 index 000000000..fbaeca999 --- /dev/null +++ b/image-scanner/Makefile @@ -0,0 +1,29 @@ + +all: build + +TAG?=latest +FLAGS= +ENVVAR= +GOOS?=darwin +REGISTRY?=686244538589.dkr.ecr.us-east-2.amazonaws.com +BASEIMAGE?=alpine:3.9 +#BUILD_NUMBER=$$(date +'%Y%m%d-%H%M%S') +#BUILD_NUMBER := $(shell bash -c 'echo $$(date +'%Y%m%d-%H%M%S')') +include $(ENV_FILE) +export + +build: clean wire + $(ENVVAR) GOOS=$(GOOS) go build -o image-scanner + +wire: + wire + +clean: + rm -rf image-scanner + +run: build + ./image-scanner + +.PHONY: build +docker-build-image: build + docker build -t image-scanner:$(TAG) . \ No newline at end of file diff --git a/image-scanner/README.md b/image-scanner/README.md new file mode 100644 index 000000000..448e5b84e --- /dev/null +++ b/image-scanner/README.md @@ -0,0 +1,2 @@ +# image-scanner + diff --git a/image-scanner/Wire.go b/image-scanner/Wire.go new file mode 100644 index 000000000..45422689a --- /dev/null +++ b/image-scanner/Wire.go @@ -0,0 +1,104 @@ +//go:build wireinject +// +build wireinject + +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "github.com/devtron-labs/common-lib/monitoring" + client "github.com/devtron-labs/common-lib/pubsub-lib" + "github.com/devtron-labs/image-scanner/api" + "github.com/devtron-labs/image-scanner/pkg/clairService" + "github.com/devtron-labs/image-scanner/pkg/grafeasService" + "github.com/devtron-labs/image-scanner/pkg/klarService" + "github.com/devtron-labs/image-scanner/pkg/logger" + "github.com/devtron-labs/image-scanner/pkg/roundTripper" + "github.com/devtron-labs/image-scanner/pkg/security" + "github.com/devtron-labs/image-scanner/pkg/sql" + "github.com/devtron-labs/image-scanner/pkg/sql/repository" + "github.com/devtron-labs/image-scanner/pkg/user" + "github.com/devtron-labs/image-scanner/pubsub" + "github.com/google/wire" +) + +func InitializeApp() (*App, error) { + wire.Build( + NewApp, + api.NewRouter, + logger.NewSugardLogger, + logger.NewHttpClient, + sql.GetConfig, + sql.NewDbConnection, + api.NewRestHandlerImpl, + wire.Bind(new(api.RestHandler), new(*api.RestHandlerImpl)), + klarService.GetKlarConfig, + grafeasService.GetGrafeasClient, + client.NewPubSubClientServiceImpl, + klarService.NewKlarServiceImpl, + wire.Bind(new(klarService.KlarService), new(*klarService.KlarServiceImpl)), + pubsub.NewNatsSubscriptionModeConfig, + pubsub.NewNatSubscription, + grafeasService.NewKlarServiceImpl, + wire.Bind(new(grafeasService.GrafeasService), new(*grafeasService.GrafeasServiceImpl)), + + clairService.GetClairConfig, + clairService.NewClairServiceImpl, + wire.Bind(new(clairService.ClairService), new(*clairService.ClairServiceImpl)), + + user.NewUserServiceImpl, + wire.Bind(new(user.UserService), new(*user.UserServiceImpl)), + repository.NewUserRepositoryImpl, + wire.Bind(new(repository.UserRepository), new(*repository.UserRepositoryImpl)), + security.GetImageScannerConfig, + security.NewImageScanServiceImpl, + wire.Bind(new(security.ImageScanService), new(*security.ImageScanServiceImpl)), + repository.NewImageScanHistoryRepositoryImpl, + wire.Bind(new(repository.ImageScanHistoryRepository), new(*repository.ImageScanHistoryRepositoryImpl)), + repository.NewImageScanResultRepositoryImpl, + wire.Bind(new(repository.ImageScanResultRepository), new(*repository.ImageScanResultRepositoryImpl)), + repository.NewImageScanObjectMetaRepositoryImpl, + wire.Bind(new(repository.ImageScanObjectMetaRepository), new(*repository.ImageScanObjectMetaRepositoryImpl)), + repository.NewCveStoreRepositoryImpl, + wire.Bind(new(repository.CveStoreRepository), new(*repository.CveStoreRepositoryImpl)), + repository.NewImageScanDeployInfoRepositoryImpl, + wire.Bind(new(repository.ImageScanDeployInfoRepository), new(*repository.ImageScanDeployInfoRepositoryImpl)), + repository.NewCiArtifactRepositoryImpl, + wire.Bind(new(repository.CiArtifactRepository), new(*repository.CiArtifactRepositoryImpl)), + repository.NewDockerArtifactStoreRepositoryImpl, + wire.Bind(new(repository.DockerArtifactStoreRepository), new(*repository.DockerArtifactStoreRepositoryImpl)), + repository.NewRegistryIndexMappingRepositoryImpl, + wire.Bind(new(repository.RegistryIndexMappingRepository), new(*repository.RegistryIndexMappingRepositoryImpl)), + + repository.NewScanToolMetadataRepositoryImpl, + wire.Bind(new(repository.ScanToolMetadataRepository), new(*repository.ScanToolMetadataRepositoryImpl)), + repository.NewScanToolStepRepositoryImpl, + wire.Bind(new(repository.ScanToolStepRepository), new(*repository.ScanToolStepRepositoryImpl)), + repository.NewScanStepConditionRepositoryImpl, + wire.Bind(new(repository.ScanStepConditionRepository), new(*repository.ScanStepConditionRepositoryImpl)), + repository.NewScanStepConditionMappingRepositoryImpl, + wire.Bind(new(repository.ScanStepConditionMappingRepository), new(*repository.ScanStepConditionMappingRepositoryImpl)), + + repository.NewScanToolExecutionHistoryMappingRepositoryImpl, + wire.Bind(new(repository.ScanToolExecutionHistoryMappingRepository), new(*repository.ScanToolExecutionHistoryMappingRepositoryImpl)), + monitoring.NewMonitoringRouter, + + roundTripper.NewRoundTripperServiceImpl, + wire.Bind(new(roundTripper.RoundTripperService), new(*roundTripper.RoundTripperServiceImpl)), + ) + return &App{}, nil +} diff --git a/image-scanner/api/RestHandler.go b/image-scanner/api/RestHandler.go new file mode 100644 index 000000000..574cba384 --- /dev/null +++ b/image-scanner/api/RestHandler.go @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package api + +import ( + "encoding/json" + "github.com/devtron-labs/image-scanner/common" + "github.com/devtron-labs/image-scanner/pkg/clairService" + "github.com/devtron-labs/image-scanner/pkg/grafeasService" + "github.com/devtron-labs/image-scanner/pkg/klarService" + "github.com/devtron-labs/image-scanner/pkg/security" + "github.com/devtron-labs/image-scanner/pkg/sql/bean" + "github.com/devtron-labs/image-scanner/pkg/sql/repository" + "github.com/devtron-labs/image-scanner/pkg/user" + "go.uber.org/zap" + "net/http" + "os" + "time" +) + +type RestHandler interface { + ScanForVulnerability(w http.ResponseWriter, r *http.Request) + ScanForVulnerabilityEvent(scanConfig *common.ImageScanEvent) (*common.ScanEventResponse, error) +} + +func NewRestHandlerImpl(logger *zap.SugaredLogger, + grafeasService grafeasService.GrafeasService, + userService user.UserService, imageScanService security.ImageScanService, + klarService klarService.KlarService, + clairService clairService.ClairService, + imageScanConfig *security.ImageScanConfig) *RestHandlerImpl { + return &RestHandlerImpl{ + Logger: logger, + grafeasService: grafeasService, + userService: userService, + ImageScanService: imageScanService, + KlarService: klarService, + ClairService: clairService, + imageScanConfig: imageScanConfig, + } +} + +type RestHandlerImpl struct { + Logger *zap.SugaredLogger + grafeasService grafeasService.GrafeasService + userService user.UserService + ImageScanService security.ImageScanService + KlarService klarService.KlarService + ClairService clairService.ClairService + imageScanConfig *security.ImageScanConfig +} +type Response struct { + Code int `json:"code,omitempty"` + Status string `json:"status,omitempty"` + Result interface{} `json:"result,omitempty"` + Errors []*ApiError `json:"errors,omitempty"` +} +type ApiError struct { + HttpStatusCode int `json:"-"` + Code string `json:"code,omitempty"` + InternalMessage string `json:"internalMessage,omitempty"` + UserMessage interface{} `json:"userMessage,omitempty"` + UserDetailMessage string `json:"userDetailMessage,omitempty"` +} + +type ResetRequest struct { + AppId int `json:"appId"` + EnvironmentId int `json:"environmentId"` +} + +func (impl *RestHandlerImpl) ScanForVulnerability(w http.ResponseWriter, r *http.Request) { + decoder := json.NewDecoder(r.Body) + var scanConfig common.ImageScanEvent + err := decoder.Decode(&scanConfig) + if err != nil { + impl.Logger.Errorw("error in decode request", "error", err) + WriteJsonResp(w, err, nil, http.StatusBadRequest) + return + } + impl.Logger.Infow("imageScan event", "scanConfig", scanConfig) + result, err := impl.ScanForVulnerabilityEvent(&scanConfig) + if err != nil { + WriteJsonResp(w, err, nil, http.StatusInternalServerError) + return + } + impl.Logger.Debugw("save", "status", result) + WriteJsonResp(w, err, result, http.StatusOK) +} + +func (impl *RestHandlerImpl) ScanForVulnerabilityEvent(scanConfig *common.ImageScanEvent) (*common.ScanEventResponse, error) { + if scanConfig.UserId == 0 { + scanConfig.UserId = 1 //setting user as system user in case of empty user data + } + impl.Logger.Infow("image scan req", "req", scanConfig) + + tool, err := impl.ImageScanService.GetActiveTool() + if err != nil { + impl.Logger.Errorw("err in image scanning", "err", err) + return nil, err + } + //creating execution history + scanEventJson, err := json.Marshal(scanConfig) + if err != nil { + impl.Logger.Errorw("error in marshalling scanEvent", "event", scanConfig, "err", err) + return nil, err + } + executionHistoryModel := &repository.ImageScanExecutionHistory{ + Image: scanConfig.Image, + ImageHash: scanConfig.ImageDigest, + ExecutionTime: time.Now(), + ExecutedBy: scanConfig.UserId, + SourceMetadataJson: string(scanEventJson), + } + executionHistory, executionHistoryDirPath, err := impl.ImageScanService.RegisterScanExecutionHistoryAndState(executionHistoryModel, tool) + if err != nil { + impl.Logger.Errorw("service err, RegisterScanExecutionHistoryAndState", "err", err) + return nil, err + } + result, err := impl.ScanImageAsPerTool(scanConfig, tool, executionHistory, executionHistoryDirPath) + if err != nil { + impl.Logger.Errorw("service err, ScanImageAsPerTool", "err", err) + return nil, err + } + //deleting executionDirectoryPath with files as well + err = os.RemoveAll(executionHistoryDirPath) + if err != nil { + impl.Logger.Errorw("error in deleting executionHistoryDirectory", "err", err) + return nil, err + } + return result, nil +} + +func (impl *RestHandlerImpl) ScanImageAsPerTool(scanConfig *common.ImageScanEvent, tool *repository.ScanToolMetadata, + executionHistory *repository.ImageScanExecutionHistory, executionHistoryDirPath string) (*common.ScanEventResponse, error) { + var result = &common.ScanEventResponse{} + imageToBeScanned, err := impl.ImageScanService.GetImageToBeScannedAndFetchCliEnv(scanConfig) + if err != nil { + impl.Logger.Errorw("service err, GetImageToBeScanned", "err", err) + return nil, err + } + scanConfig.Image = imageToBeScanned + if tool.Name == bean.ScanToolClair && tool.Version == bean.ScanToolVersion2 { + result, err = impl.KlarService.Process(scanConfig, executionHistory) + if err != nil { + impl.Logger.Errorw("err in process msg", "err", err) + return nil, err + } + } else if tool.Name == bean.ScanToolClair && tool.Version == bean.ScanToolVersion4 { + result, err = impl.ClairService.ScanImage(scanConfig, tool, executionHistory) + if err != nil { + impl.Logger.Errorw("err in process msg", "err", err) + return nil, err + } + } else { + err = impl.ImageScanService.ScanImage(scanConfig, tool, executionHistory, executionHistoryDirPath) + if err != nil { + impl.Logger.Errorw("err in process msg", "err", err) + return nil, err + } + } + return result, nil +} diff --git a/image-scanner/api/Router.go b/image-scanner/api/Router.go new file mode 100644 index 000000000..b311f51b7 --- /dev/null +++ b/image-scanner/api/Router.go @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package api + +import ( + "encoding/json" + "github.com/devtron-labs/common-lib/monitoring" + "github.com/gorilla/mux" + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.uber.org/zap" + "net/http" +) + +type Router struct { + logger *zap.SugaredLogger + Router *mux.Router + restHandler RestHandler + monitoringRouter *monitoring.MonitoringRouter +} + +func NewRouter(logger *zap.SugaredLogger, restHandler RestHandler, monitoringRouter *monitoring.MonitoringRouter) *Router { + return &Router{logger: logger, Router: mux.NewRouter(), restHandler: restHandler, monitoringRouter: monitoringRouter} +} + +func (r Router) Init() { + r.Router.StrictSlash(true) + pProfListenerRouter := r.Router.PathPrefix("/image-scanner/debug/pprof/").Subrouter() + statsVizRouter := r.Router.PathPrefix("/image-scanner").Subrouter() + r.monitoringRouter.InitMonitoringRouter(pProfListenerRouter, statsVizRouter, "/image-scanner") + r.Router.Handle("/metrics", promhttp.Handler()) + r.Router.Path("/health").HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(200) + response := Response{} + response.Code = 200 + response.Result = "OK" + b, err := json.Marshal(response) + if err != nil { + b = []byte("OK") + r.logger.Errorw("Unexpected error in apiError", "err", err) + } + _, _ = writer.Write(b) + }) + + r.Router.Path("/scanner/image").HandlerFunc(r.restHandler.ScanForVulnerability).Methods("POST") +} diff --git a/image-scanner/api/apiError.go b/image-scanner/api/apiError.go new file mode 100644 index 000000000..627d0ee4a --- /dev/null +++ b/image-scanner/api/apiError.go @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package api + +import ( + "encoding/json" + "github.com/devtron-labs/image-scanner/internals/util" + "github.com/juju/errors" + "net/http" +) + +// use of WriteJsonRespStructured is preferable. it api exists due to historical reason +// err.message is used as internals message for ApiError object in resp +func WriteJsonResp(w http.ResponseWriter, err error, respBody interface{}, status int) { + response := ResponseV2{} + if err == nil { + response.Result = respBody + } else if apiErr, ok := err.(*util.ApiError); ok { + response.Errors = []*util.ApiError{apiErr} + if apiErr.HttpStatusCode != 0 { + status = apiErr.HttpStatusCode + } + } else if util.IsErrNoRows(err) { + status = http.StatusNotFound + apiErr := &util.ApiError{} + apiErr.Code = "000" // 000=unknown + apiErr.InternalMessage = errors.Details(err) + if respBody != nil { + apiErr.UserMessage = respBody + } else { + apiErr.UserMessage = err.Error() + } + response.Errors = []*util.ApiError{apiErr} + } else { + apiErr := &util.ApiError{} + apiErr.Code = "000" // 000=unknown + apiErr.InternalMessage = errors.Details(err) + if respBody != nil { + apiErr.UserMessage = respBody + } else { + apiErr.UserMessage = err.Error() + } + response.Errors = []*util.ApiError{apiErr} + } + response.Code = status //TODO : discuss with prashant about http status header + response.Status = http.StatusText(status) + + b, err := json.Marshal(response) + if err != nil { + util.GetLogger().Errorw("error in marshaling err object", "err", err) + status = 500 + + response := ResponseV2{} + apiErr := &util.ApiError{} + apiErr.Code = "0000" // 000=unknown + apiErr.InternalMessage = errors.Details(err) + apiErr.UserMessage = "response marshaling error" + response.Errors = []*util.ApiError{apiErr} + b, err = json.Marshal(response) + if err != nil { + b = []byte("response marshaling error") + util.GetLogger().Errorw("Unexpected error in apiError", "err", err) + } + } + if status > 299 || err != nil { + util.GetLogger().Infow("ERROR RES", "TYPE", "API-ERROR", "RES", response.Code, "ERROR-MSG", response.Errors, "err", err) + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + w.Write(b) +} + +// use this method when we have specific api error to be conveyed to api User +func WriteJsonRespStructured(w http.ResponseWriter, err error, respBody interface{}, status int, apiErrors []*util.ApiError) { + response := ResponseV2{} + response.Code = status + response.Status = http.StatusText(status) + if err == nil { + response.Result = respBody + } else { + response.Errors = apiErrors + } + b, err := json.Marshal(response) + if err != nil { + util.GetLogger().Error("error in marshaling err object", err) + status = 500 + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + w.Write(b) +} + +// global response body used across api +type ResponseV2 struct { + Code int `json:"code,omitempty"` + Status string `json:"status,omitempty"` + Result interface{} `json:"result,omitempty"` + Errors []*util.ApiError `json:"errors,omitempty"` +} + +func contains(s []*string, e *string) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} diff --git a/image-scanner/common/bean.go b/image-scanner/common/bean.go new file mode 100644 index 000000000..31f5bfb74 --- /dev/null +++ b/image-scanner/common/bean.go @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package common + +import ( + git "github.com/devtron-labs/common-lib/git-manager" + "github.com/optiopay/klar/clair" + "github.com/quay/claircore" + "strings" + "time" +) + +const ( + AWSAccessKeyId = "AWS_ACCESS_KEY_ID" + AWSSecretAccessKey = "AWS_SECRET_ACCESS_KEY" + AWSRegion = "AWS_DEFAULT_REGION" + Username = "USERNAME" + Password = "PASSWORD" + GCR_FILE_PATH = "FILE_PATH" + IMAGE_NAME = "IMAGE_NAME" + OUTPUT_FILE_PATH = "OUTPUT_FILE_PATH" + EXTRA_ARGS = "EXTRA_ARGS" + CA_CERT_FILE_PATH = "CA_CERT_FILE_PATH" +) + +const ( + SHELL_COMMAND = "sh" + COMMAND_ARGS = "-c" +) + +const ( + CaCertDirectory = "security/certs" + RegistryCaCertFilePrefix = "registry-ca-cert-" +) + +type RegistryType string + +const ( + INSECURE = "insecure" + SECUREWITHCERT = "secure-with-cert" +) + +type ImageScanRenderDto struct { + RegistryType RegistryType `json:"-"` + AWSAccessKeyId string `json:"awsAccessKeyId,omitempty" ` + AWSSecretAccessKey string `json:"awsSecretAccessKey,omitempty"` + AWSRegion string `json:"awsRegion"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Image string `json:"image"` + OutputFilePath string `json:"-"` + CaCertFilePath string `json:"-"` + DockerConnection string `json:"-"` +} + +type ImageScanEvent struct { + Image string `json:"image"` + ImageDigest string `json:"imageDigest"` + AppId int `json:"appId"` + EnvId int `json:"envId"` + PipelineId int `json:"pipelineId"` + CiArtifactId int `json:"ciArtifactId"` + UserId int `json:"userId"` + AccessKey string `json:"accessKey"` + SecretKey string `json:"secretKey"` + Token string `json:"token"` + AwsRegion string `json:"awsRegion"` + DockerRegistryId string `json:"dockerRegistryId"` + DockerConnection string `json:"dockerConnection"` + DockerCert string `json:"dockerCert"` + CiProjectDetails []git.CiProjectDetails `json:"ciProjectDetails"` + SourceType SourceType `json:"sourceType"` + SourceSubType SourceSubType `json:"sourceSubType"` + CiWorkflowId int `json:"ciWorkflowId"` + CdWorkflowId int `json:"cdWorkflowId"` + ChartHistoryId int `json:"chartHistoryId"` + ManifestData *ManifestData `json:"manifestData"` + ReScan bool `json:"reScan"` +} + +func (r *ImageScanEvent) IsManifest() bool { + return r.SourceType == SourceTypeCode && r.SourceSubType == SourceSubTypeManifest +} + +type ScanEventResponse struct { + RequestData *ImageScanEvent `json:"requestData"` + ResponseDataClairV4 []*claircore.Vulnerability `json:"responseDataClairV4"` + ResponseDataClairV2 []*clair.Vulnerability `json:"ResponseDataClairV2"` + CodeScanRes interface{} `json:"codeScanResponse"` +} + +const ( + ScanObjectType_APP string = "app" + ScanObjectType_CHART string = "chart" + ScanObjectType_POD string = "pod" +) + +type ImageScanRequest struct { + ScanExecutionId int `json:"ScanExecutionId"` + ImageScanDeployInfoId int `json:"imageScanDeployInfo"` + AppId int `json:"appId"` + EnvId int `json:"envId"` + ArtifactId int `json:"artifactId"` + CVEName string `json:"CveName"` + Image string `json:"image"` + ViewFor int `json:"viewFor"` + Offset int `json:"offset"` + Size int `json:"size"` + PipelineId int `json:"pipelineId"` + PipelineType string `json:"pipelineType"` +} + +type ImageScanHistoryListingResponse struct { + Offset int `json:"offset"` + Size int `json:"size"` + ImageScanHistoryResponse []*ImageScanHistoryResponse `json:"scanList"` +} + +type ImageScanHistoryResponse struct { + ImageScanDeployInfoId int `json:"imageScanDeployInfoId"` + AppId int `json:"appId"` + EnvId int `json:"envId"` + Name string `json:"name"` + Type string `json:"type"` + Environment string `json:"environment"` + LastChecked time.Time `json:"lastChecked"` + Image string `json:"image,omitempty"` + SeverityCount *SeverityCount `json:"severityCount,omitempty"` +} + +type ImageScanExecutionDetail struct { + ImageScanDeployInfoId int `json:"imageScanDeployInfoId"` + AppId int `json:"appId,omitempty"` + EnvId int `json:"envId,omitempty"` + AppName string `json:"appName,omitempty"` + EnvName string `json:"envName,omitempty"` + ArtifactId int `json:"artifactId,omitempty"` + Image string `json:"image,omitempty"` + PodName string `json:"podName,omitempty"` + ReplicaSet string `json:"replicaSet,omitempty"` + Vulnerabilities []*Vulnerabilities `json:"vulnerabilities,omitempty"` + SeverityCount *SeverityCount `json:"severityCount,omitempty"` + ExecutionTime time.Time `json:"executionTime,omitempty"` +} + +type Vulnerabilities struct { + CVEName string `json:"cveName"` + Severity string `json:"severity"` + Package string `json:"package"` + CVersion string `json:"currentVersion"` + FVersion string `json:"fixedVersion"` + Permission string `json:"permission"` +} + +type SeverityCount struct { + High int `json:"high"` + Moderate int `json:"moderate"` + Low int `json:"low"` +} + +func RemoveTrailingComma(jsonString string) string { + if strings.HasSuffix(jsonString, ",]") { + return jsonString[:len(jsonString)-2] + jsonString[len(jsonString)-1:] + } + return jsonString +} + +// multiple history rows for one source event +type SourceType int + +const ( + SourceTypeImage SourceType = 1 + SourceTypeCode SourceType = 2 + SourceTypeSbom SourceType = 3 // can be used in future for direct sbom scanning +) + +type SourceSubType int + +const ( + SourceSubTypeCi SourceSubType = 1 // relevant for ci code(2,1) or ci built image(1,1) + SourceSubTypeManifest SourceSubType = 2 // relevant for devtron app deployment manifest/helm app manifest(2,2) or images retrieved from manifest(1,2)) +) + +type ManifestData struct { + ChartData []byte `json:"chartData"` + ValuesYaml []byte `json:"valuesYaml"` +} diff --git a/image-scanner/config.md b/image-scanner/config.md new file mode 100644 index 000000000..9fbe61721 --- /dev/null +++ b/image-scanner/config.md @@ -0,0 +1,14 @@ +# IMAGESCANER CONFIGMAP + + +| Variable Name | Value | Description | +|---------------------|----------------------------------------|-------------------------------| +| CLAIR_ADDR | clair-dcd.devtroncd:6060 | For connecting to Clair if it's enabled | +| CLIENT_ID | client-2 | Client ID | +| NATS_SERVER_HOST | nats://devtron-nats.devtroncd:4222 | For connecting to NATS | +| PG_LOG_QUERY | "false" | PostgreSQL Query Logging (false to disable) | +| PG_ADDR | postgresql-postgresql.devtroncd | PostgreSQL Server Address | +| PG_DATABASE | orchestrator | PostgreSQL Database Name | +| PG_PORT | "5432" | PostgreSQL Port Number | +| PG_USER | postgres | PostgreSQL User Name | + diff --git a/image-scanner/go.mod b/image-scanner/go.mod new file mode 100644 index 000000000..ade4f3d55 --- /dev/null +++ b/image-scanner/go.mod @@ -0,0 +1,77 @@ +module github.com/devtron-labs/image-scanner + +go 1.21 + +toolchain go1.21.3 + +require ( + github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible + github.com/antihax/optional v1.0.0 + github.com/aws/aws-sdk-go v1.44.116 + github.com/caarlos0/env v3.5.0+incompatible + github.com/caarlos0/env/v6 v6.10.1 + github.com/devtron-labs/common-lib v0.19.0 + github.com/go-pg/pg v6.15.1+incompatible + github.com/google/go-containerregistry v0.6.0 + github.com/google/wire v0.6.0 + github.com/gorilla/mux v1.8.0 + github.com/juju/errors v0.0.0-20210818161939-5560c4c073ff + github.com/optiopay/klar v2.4.0+incompatible + github.com/prometheus/client_golang v1.16.0 + github.com/quay/claircore v1.3.1 + github.com/tidwall/gjson v1.14.4 + go.uber.org/zap v1.21.0 + golang.org/x/oauth2 v0.21.0 +) + +require ( + cloud.google.com/go/compute/metadata v0.3.0 // indirect + github.com/arl/statsviz v0.6.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/coreos/clair v2.0.1-0.20171220021131-30bd568d8361+incompatible // indirect + github.com/docker/cli v24.0.6+incompatible // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/docker v27.2.0+incompatible // indirect + github.com/docker/docker-credential-helpers v0.7.0 // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/klauspost/compress v1.16.7 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/minio/highwayhash v1.0.2 // indirect + github.com/nats-io/jwt/v2 v2.5.3 // indirect + github.com/nats-io/nats.go v1.28.0 // indirect + github.com/nats-io/nkeys v0.4.6 // indirect + github.com/nats-io/nuid v1.0.1 // indirect + github.com/onsi/ginkgo v1.16.5 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.11.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/grpc v1.58.3 // indirect + google.golang.org/protobuf v1.33.0 // indirect + mellium.im/sasl v0.3.1 // indirect +) + +replace github.com/devtron-labs/common-lib => github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241024135802-b4888f54a136 diff --git a/image-scanner/go.sum b/image-scanner/go.sum new file mode 100644 index 000000000..7ecef703a --- /dev/null +++ b/image-scanner/go.sum @@ -0,0 +1,1676 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +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= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v19.1.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v10.15.5+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= +github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= +github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= +github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= +github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= +github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +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/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46/go.mod h1:olhPNdiiAAMiSujemd1O/sc6GcyePr23f/6uGKtthNg= +github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492/go.mod h1:9Beu8XsUNNfzml7WBf3QmyPToP1wm1Gj/Vc5UJKqTzU= +github.com/arl/statsviz v0.6.0 h1:jbW1QJkEYQkufd//4NDYRSNBpwJNrdzPahF7ZmoGdyE= +github.com/arl/statsviz v0.6.0/go.mod h1:0toboo+YGSUXDaS4g1D5TVS4dXs7S7YYT5J/qnW2h8s= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/aws/aws-sdk-go v1.15.90/go.mod h1:es1KtYUFs7le0xQ3rOihkuoVD90z7D0fR2Qm4S00/gU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.44.116 h1:NpLIhcvLWXJZAEwvPj3TDHeqp7DleK6ZUVYyW01WNHY= +github.com/aws/aws-sdk-go v1.44.116/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +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= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yixi/rBrKyJs= +github.com/caarlos0/env v3.5.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y= +github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II= +github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +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.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= +github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= +github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= +github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= +github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= +github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= +github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= +github.com/containerd/containerd v1.5.2/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= +github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= +github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= +github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= +github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= +github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= +github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= +github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= +github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= +github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= +github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/stargz-snapshotter/estargz v0.7.0 h1:1d/rydzTywc76lnjJb6qbPCiTiCwts49AzKps/Ecblw= +github.com/containerd/stargz-snapshotter/estargz v0.7.0/go.mod h1:83VWDqHnurTKliEB0YvWMiCfLDwv4Cjj1X9Vk98GJZw= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= +github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= +github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= +github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= +github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= +github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= +github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= +github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= +github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= +github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/clair v2.0.1-0.20171220021131-30bd568d8361+incompatible h1:OM4/5dsd9CSXNWkPJsAw3AB9mTt8FH6q3aUx8LYbRgU= +github.com/coreos/clair v2.0.1-0.20171220021131-30bd568d8361+incompatible/go.mod h1:uXhHPWAoRqw0jJc2f8RrPCwRhIo9otQ8OEWUFtpCiwA= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/crgimenes/goconfig v1.2.1/go.mod h1:NLkiEPjGZF4p1jzt3S7stOW7z/MJqvCRwJuDmC7b8fw= +github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241024135802-b4888f54a136 h1:rNGxjU5L6NvObxGMt0+vNFmjkqstm7zDASiS+pakrgQ= +github.com/devtron-labs/devtron-services/common-lib v0.0.0-20241024135802-b4888f54a136/go.mod h1:KpKnF4OSpQNDJmb4wVZq3Za88ePBw4xec2GOAGRm5UQ= +github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= +github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= +github.com/docker/distribution v2.6.0-rc.1.0.20180327202408-83389a148052+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.2.0+incompatible h1:Rk9nIVdfH3+Vz4cyI/uhbINhEZ/oLmc+CBXmH6fbNk4= +github.com/docker/docker v27.2.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= +github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/doug-martin/goqu/v8 v8.6.0/go.mod h1:wiiYWkiguNXK5d4kGIkYmOxBScEL37d9Cfv9tXhPsTk= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +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-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +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/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +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 v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-pg/pg v6.15.1+incompatible h1:vO4P9WoCi+i4qomgcBXWlKgDk4GcHAqDAOIfkEpi7B4= +github.com/go-pg/pg v6.15.1+incompatible/go.mod h1:a2oXow+aFOrvwcKs3eIA0lNFmMilrxK2sOkB5NWe0vA= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= +github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-containerregistry v0.0.0-20191206185556-eb7c14b719c6/go.mod h1:rodaC7jYStJ2mjR8Y+5a/jCzcRPFRH74KmqSnJC88co= +github.com/google/go-containerregistry v0.6.0 h1:niQ+8XD//kKgArIFwDVBXsWVWbde16LPdHMyNwSC8h4= +github.com/google/go-containerregistry v0.6.0/go.mod h1:euCCtNbZ6tKqi1E72vwDj2xZcN5ttKpZLfa/wSo5iLw= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +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/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= +github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= +github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.10.0 h1:4EYhlDVEMsJ30nNj0mmgwIUXoq7e9sMJrVC2ED6QlCU= +github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.8.1 h1:9k0IXtdJXHJbyAWQgbWr1lU+MEhPXZz6RIXxfR5oxXs= +github.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.13.0 h1:JCjhT5vmhMAf/YwBHLvrBn4OGdIQBiFG6ym8Zmdx570= +github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3 h1:JnPg/5Q9xVJGfjsO5CPUOjnJps1JaRUm8I9FXVCFK94= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/errors v0.0.0-20210818161939-5560c4c073ff h1:WLHwK6yMswDvGUNrkxp4GYnrbQS8WULu1D3qteVdUIg= +github.com/juju/errors v0.0.0-20210818161939-5560c4c073ff/go.mod h1:i1eL7XREII6aHpQ2gApI/v6FkVUDEBremNkcBCKYAcY= +github.com/juju/loggo v0.0.0-20170605014607-8232ab8918d9 h1:Y+lzErDTURqeXqlqYi4YBYbDd7ycU74gW1ADt57/bgY= +github.com/juju/loggo v0.0.0-20170605014607-8232ab8918d9/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20180517134105-72703b1e95eb h1:oHBF98WnC1lqCv/W7I7gxnLjD6xJieJ8yt/3IrnMthY= +github.com/juju/testing v0.0.0-20180517134105-72703b1e95eb/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.10.6/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.0/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f/go.mod h1:q59u9px8b7UTj0nIjEjvmTWekazka6xIt6Uogz5Dm+8= +github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d/go.mod h1:o8sgWoz3JADecfc/cTYD92/Et1yMqMy0utV1z+VaZao= +github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/jwt/v2 v2.5.3 h1:/9SWvzc6hTfamcgXJ3uYRpgj+QuY2aLNqRiqrKcrpEo= +github.com/nats-io/jwt/v2 v2.5.3/go.mod h1:iysuPemFcc7p4IoYots3IuELSI4EDe9Y0bQMe+I3Bf4= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats-server/v2 v2.9.23 h1:6Wj6H6QpP9FMlpCyWUaNu2yeZ/qGj+mdRkZ1wbikExU= +github.com/nats-io/nats-server/v2 v2.9.23/go.mod h1:wEjrEy9vnqIGE4Pqz4/c75v9Pmaq7My2IgFmnykc4C0= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nats.go v1.28.0 h1:Th4G6zdsz2d0OqXdfzKLClo6bOfoI/b1kInhRtFIy5c= +github.com/nats-io/nats.go v1.28.0/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.4.6 h1:IzVe95ru2CT6ta874rt9saQRkWfe2nFj1NtvYSLqMzY= +github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADymtkpts= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= +github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/optiopay/klar v2.4.0+incompatible h1:vEiIduz/RRH4D30s7w3vqMaWmCr350VCCn08qd7JLPk= +github.com/optiopay/klar v2.4.0+incompatible/go.mod h1:YfdqA28is7ktrml8lXBVobAbVarjZYEEYs1DGsTTQQY= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/quay/alas v1.0.1/go.mod h1:pseepSrG9pwry1joG7RO/RNRFJaWqiqx9qeoomeYwEk= +github.com/quay/claircore v1.3.1 h1:fmQISWVg2Bc59Id2BEaGZIX2rlDsR6ciOlC8h9YLj28= +github.com/quay/claircore v1.3.1/go.mod h1:Y237zhvAdUfoe0LpJx182P1+WZSt0bVd3YvKbMESe8k= +github.com/quay/goval-parser v0.8.6 h1:h1Xg3SZR/6I7UVa1LcsQZvQft/q7sJbosmFrjzSmdqE= +github.com/quay/goval-parser v0.8.6/go.mod h1:Y0NTNfPYOC7yxsYKzJOrscTWUPq1+QbtHw4XpPXWPMc= +github.com/quay/zlog v1.1.0 h1:H+41uHbt3qugJRJfo3aDYpRgJRrcjC32/VH+qs5Jdig= +github.com/quay/zlog v1.1.0/go.mod h1:szs9k88lsac48+Wm6QTnpObO67tu0oMr/p5V6qmPEIw= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/remind101/migrate v0.0.0-20170729031349-52c1edff7319/go.mod h1:rhSvwcijY9wfmrBYrfCvapX8/xOTV46NAUjBRgUyJqc= +github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= +github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE= +github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +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/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +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.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= +github.com/tidwall/gjson v1.14.4/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.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= +github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/otel v1.1.0/go.mod h1:7cww0OW51jQ8IaZChIEdqLwgh+44+7uiTdWsAL0wQpA= +go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/trace v1.1.0/go.mod h1:i47XtdcBQiktu5IsrPqOHe8w+sBmnLwwHt8wiUsWGTI= +go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +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/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +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/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/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-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190924052046-3ac2a5bbd98a/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-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/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= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191205215504-7b8c8591a921/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= +google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +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-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= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= +google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e h1:z3vDksarJxsAKM5dmEGv0GHwE2hKJ096wZra71Vs4sw= +google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +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.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +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-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4 h1:hILp2hNrRnYjZpmIbx70psAHbBSEcQ1NIzDcUbJ1b6g= +gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170712054546-1be3d31502d6/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +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/api v0.0.0-20180904230853-4e7be11eab3f/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= +k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= +k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= +k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= +k8s.io/apimachinery v0.0.0-20180904193909-def12e63c512/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= +k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= +k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= +k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= +k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= +k8s.io/client-go v0.0.0-20180910083459-2cefa64ff137/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= +k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= +k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= +k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= +k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= +k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= +k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= +k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= +k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kubernetes v1.11.10/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo= +mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= +modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/image-scanner/grafeas/.gitignore b/image-scanner/grafeas/.gitignore new file mode 100644 index 000000000..daf913b1b --- /dev/null +++ b/image-scanner/grafeas/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/image-scanner/grafeas/.swagger-codegen-ignore b/image-scanner/grafeas/.swagger-codegen-ignore new file mode 100644 index 000000000..c5fa491b4 --- /dev/null +++ b/image-scanner/grafeas/.swagger-codegen-ignore @@ -0,0 +1,23 @@ +# Swagger Codegen Ignore +# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/image-scanner/grafeas/.swagger-codegen/VERSION b/image-scanner/grafeas/.swagger-codegen/VERSION new file mode 100644 index 000000000..26f8b8bcd --- /dev/null +++ b/image-scanner/grafeas/.swagger-codegen/VERSION @@ -0,0 +1 @@ +2.4.5 \ No newline at end of file diff --git a/image-scanner/grafeas/.travis.yml b/image-scanner/grafeas/.travis.yml new file mode 100644 index 000000000..f5cb2ce9a --- /dev/null +++ b/image-scanner/grafeas/.travis.yml @@ -0,0 +1,8 @@ +language: go + +install: + - go get -d -v . + +script: + - go build -v ./ + diff --git a/image-scanner/grafeas/README.md b/image-scanner/grafeas/README.md new file mode 100644 index 000000000..200e90e75 --- /dev/null +++ b/image-scanner/grafeas/README.md @@ -0,0 +1,145 @@ +# Go API client for grafeas + +No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + +## Overview +This API client was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the [swagger-spec](https://github.com/swagger-api/swagger-spec) from a remote server, you can easily generate an API client. + +- API version: version not set +- Package version: 0.1.4 +- Build package: io.swagger.codegen.languages.GoClientCodegen + +## Installation +Put the package under your project folder and add the following in import: +```golang +import "./grafeas" +``` + +## Documentation for API Endpoints + +All URIs are relative to *http://localhost* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +*GrafeasV1Beta1Api* | [**BatchCreateNotes**](docs/GrafeasV1Beta1Api.md#batchcreatenotes) | **Post** /v1beta1/{parent=projects/*}/notes:batchCreate | Creates new notes in batch. +*GrafeasV1Beta1Api* | [**BatchCreateOccurrences**](docs/GrafeasV1Beta1Api.md#batchcreateoccurrences) | **Post** /v1beta1/{parent=projects/*}/occurrences:batchCreate | Creates new occurrences in batch. +*GrafeasV1Beta1Api* | [**CreateNote**](docs/GrafeasV1Beta1Api.md#createnote) | **Post** /v1beta1/{parent=projects/*}/notes | Creates a new note. +*GrafeasV1Beta1Api* | [**CreateOccurrence**](docs/GrafeasV1Beta1Api.md#createoccurrence) | **Post** /v1beta1/{parent=projects/*}/occurrences | Creates a new occurrence. +*GrafeasV1Beta1Api* | [**DeleteNote**](docs/GrafeasV1Beta1Api.md#deletenote) | **Delete** /v1beta1/{name=projects/*/notes/*} | Deletes the specified note. +*GrafeasV1Beta1Api* | [**DeleteOccurrence**](docs/GrafeasV1Beta1Api.md#deleteoccurrence) | **Delete** /v1beta1/{name=projects/*/occurrences/*} | Deletes the specified occurrence. For example, use this method to delete an occurrence when the occurrence is no longer applicable for the given resource. +*GrafeasV1Beta1Api* | [**GetNote**](docs/GrafeasV1Beta1Api.md#getnote) | **Get** /v1beta1/{name=projects/*/notes/*} | Gets the specified note. +*GrafeasV1Beta1Api* | [**GetOccurrence**](docs/GrafeasV1Beta1Api.md#getoccurrence) | **Get** /v1beta1/{name=projects/*/occurrences/*} | Gets the specified occurrence. +*GrafeasV1Beta1Api* | [**GetOccurrenceNote**](docs/GrafeasV1Beta1Api.md#getoccurrencenote) | **Get** /v1beta1/{name=projects/*/occurrences/*}/notes | Gets the note attached to the specified occurrence. Consumer projects can use this method to get a note that belongs to a provider project. +*GrafeasV1Beta1Api* | [**GetVulnerabilityOccurrencesSummary**](docs/GrafeasV1Beta1Api.md#getvulnerabilityoccurrencessummary) | **Get** /v1beta1/{parent=projects/*}/occurrences:vulnerabilitySummary | Gets a summary of the number and severity of occurrences. +*GrafeasV1Beta1Api* | [**ListNoteOccurrences**](docs/GrafeasV1Beta1Api.md#listnoteoccurrences) | **Get** /v1beta1/{name=projects/*/notes/*}/occurrences | Lists occurrences referencing the specified note. Provider projects can use this method to get all occurrences across consumer projects referencing the specified note. +*GrafeasV1Beta1Api* | [**ListNotes**](docs/GrafeasV1Beta1Api.md#listnotes) | **Get** /v1beta1/{parent=projects/*}/notes | Lists notes for the specified project. +*GrafeasV1Beta1Api* | [**ListOccurrences**](docs/GrafeasV1Beta1Api.md#listoccurrences) | **Get** /v1beta1/{parent=projects/*}/occurrences | Lists occurrences for the specified project. +*GrafeasV1Beta1Api* | [**UpdateNote**](docs/GrafeasV1Beta1Api.md#updatenote) | **Patch** /v1beta1/{name=projects/*/notes/*} | Updates the specified note. +*GrafeasV1Beta1Api* | [**UpdateOccurrence**](docs/GrafeasV1Beta1Api.md#updateoccurrence) | **Patch** /v1beta1/{name=projects/*/occurrences/*} | Updates the specified occurrence. + + +## Documentation For Models + + - [AliasContextKind](docs/AliasContextKind.md) + - [AttestationAttestation](docs/AttestationAttestation.md) + - [AttestationAuthority](docs/AttestationAuthority.md) + - [AttestationGenericSignedAttestation](docs/AttestationGenericSignedAttestation.md) + - [AttestationGenericSignedAttestationContentType](docs/AttestationGenericSignedAttestationContentType.md) + - [AttestationPgpSignedAttestation](docs/AttestationPgpSignedAttestation.md) + - [AttestationPgpSignedAttestationContentType](docs/AttestationPgpSignedAttestationContentType.md) + - [AuthorityHint](docs/AuthorityHint.md) + - [BuildBuild](docs/BuildBuild.md) + - [BuildBuildSignature](docs/BuildBuildSignature.md) + - [BuildSignatureKeyType](docs/BuildSignatureKeyType.md) + - [CvsSv3AttackComplexity](docs/CvsSv3AttackComplexity.md) + - [CvsSv3AttackVector](docs/CvsSv3AttackVector.md) + - [CvsSv3Impact](docs/CvsSv3Impact.md) + - [CvsSv3PrivilegesRequired](docs/CvsSv3PrivilegesRequired.md) + - [CvsSv3Scope](docs/CvsSv3Scope.md) + - [CvsSv3UserInteraction](docs/CvsSv3UserInteraction.md) + - [DeploymentDeployable](docs/DeploymentDeployable.md) + - [DeploymentDeployment](docs/DeploymentDeployment.md) + - [DeploymentPlatform](docs/DeploymentPlatform.md) + - [DiscoveredAnalysisStatus](docs/DiscoveredAnalysisStatus.md) + - [DiscoveredContinuousAnalysis](docs/DiscoveredContinuousAnalysis.md) + - [DiscoveryDiscovered](docs/DiscoveryDiscovered.md) + - [DiscoveryDiscovery](docs/DiscoveryDiscovery.md) + - [Grafeasv1beta1Signature](docs/Grafeasv1beta1Signature.md) + - [HashHashType](docs/HashHashType.md) + - [ImageBasis](docs/ImageBasis.md) + - [ImageDerived](docs/ImageDerived.md) + - [ImageFingerprint](docs/ImageFingerprint.md) + - [ImageLayer](docs/ImageLayer.md) + - [InTotoArtifactRule](docs/InTotoArtifactRule.md) + - [IntotoInToto](docs/IntotoInToto.md) + - [IntotoLink](docs/IntotoLink.md) + - [IntotoLinkArtifact](docs/IntotoLinkArtifact.md) + - [IntotoSigningKey](docs/IntotoSigningKey.md) + - [LayerDirective](docs/LayerDirective.md) + - [LinkArtifactHashes](docs/LinkArtifactHashes.md) + - [LinkByProducts](docs/LinkByProducts.md) + - [LinkEnvironment](docs/LinkEnvironment.md) + - [PackageArchitecture](docs/PackageArchitecture.md) + - [PackageDistribution](docs/PackageDistribution.md) + - [PackageInstallation](docs/PackageInstallation.md) + - [PackagePackage](docs/PackagePackage.md) + - [PackageVersion](docs/PackageVersion.md) + - [ProtobufAny](docs/ProtobufAny.md) + - [ProtobufFieldMask](docs/ProtobufFieldMask.md) + - [ProvenanceBuildProvenance](docs/ProvenanceBuildProvenance.md) + - [ProvenanceCommand](docs/ProvenanceCommand.md) + - [ProvenanceFileHashes](docs/ProvenanceFileHashes.md) + - [ProvenanceHash](docs/ProvenanceHash.md) + - [ProvenanceSource](docs/ProvenanceSource.md) + - [RpcStatus](docs/RpcStatus.md) + - [SourceAliasContext](docs/SourceAliasContext.md) + - [SourceCloudRepoSourceContext](docs/SourceCloudRepoSourceContext.md) + - [SourceGerritSourceContext](docs/SourceGerritSourceContext.md) + - [SourceGitSourceContext](docs/SourceGitSourceContext.md) + - [SourceProjectRepoId](docs/SourceProjectRepoId.md) + - [SourceRepoId](docs/SourceRepoId.md) + - [SourceSourceContext](docs/SourceSourceContext.md) + - [V1beta1BatchCreateNotesRequest](docs/V1beta1BatchCreateNotesRequest.md) + - [V1beta1BatchCreateNotesResponse](docs/V1beta1BatchCreateNotesResponse.md) + - [V1beta1BatchCreateOccurrencesRequest](docs/V1beta1BatchCreateOccurrencesRequest.md) + - [V1beta1BatchCreateOccurrencesResponse](docs/V1beta1BatchCreateOccurrencesResponse.md) + - [V1beta1ListNoteOccurrencesResponse](docs/V1beta1ListNoteOccurrencesResponse.md) + - [V1beta1ListNotesResponse](docs/V1beta1ListNotesResponse.md) + - [V1beta1ListOccurrencesResponse](docs/V1beta1ListOccurrencesResponse.md) + - [V1beta1Note](docs/V1beta1Note.md) + - [V1beta1NoteKind](docs/V1beta1NoteKind.md) + - [V1beta1Occurrence](docs/V1beta1Occurrence.md) + - [V1beta1RelatedUrl](docs/V1beta1RelatedUrl.md) + - [V1beta1Resource](docs/V1beta1Resource.md) + - [V1beta1VulnerabilityOccurrencesSummary](docs/V1beta1VulnerabilityOccurrencesSummary.md) + - [V1beta1attestationDetails](docs/V1beta1attestationDetails.md) + - [V1beta1buildDetails](docs/V1beta1buildDetails.md) + - [V1beta1deploymentDetails](docs/V1beta1deploymentDetails.md) + - [V1beta1discoveryDetails](docs/V1beta1discoveryDetails.md) + - [V1beta1imageDetails](docs/V1beta1imageDetails.md) + - [V1beta1intotoDetails](docs/V1beta1intotoDetails.md) + - [V1beta1intotoSignature](docs/V1beta1intotoSignature.md) + - [V1beta1packageDetails](docs/V1beta1packageDetails.md) + - [V1beta1packageLocation](docs/V1beta1packageLocation.md) + - [V1beta1provenanceArtifact](docs/V1beta1provenanceArtifact.md) + - [V1beta1vulnerabilityDetails](docs/V1beta1vulnerabilityDetails.md) + - [VersionVersionKind](docs/VersionVersionKind.md) + - [VulnerabilityCvsSv3](docs/VulnerabilityCvsSv3.md) + - [VulnerabilityDetail](docs/VulnerabilityDetail.md) + - [VulnerabilityOccurrencesSummaryFixableTotalByDigest](docs/VulnerabilityOccurrencesSummaryFixableTotalByDigest.md) + - [VulnerabilityPackageIssue](docs/VulnerabilityPackageIssue.md) + - [VulnerabilitySeverity](docs/VulnerabilitySeverity.md) + - [VulnerabilityVulnerability](docs/VulnerabilityVulnerability.md) + - [VulnerabilityVulnerabilityLocation](docs/VulnerabilityVulnerabilityLocation.md) + - [VulnerabilityWindowsDetail](docs/VulnerabilityWindowsDetail.md) + - [WindowsDetailKnowledgeBase](docs/WindowsDetailKnowledgeBase.md) + + +## Documentation For Authorization + Endpoints do not require authorization. + + +## Author + + + diff --git a/image-scanner/grafeas/api/swagger.yaml b/image-scanner/grafeas/api/swagger.yaml new file mode 100644 index 000000000..b3c3104f0 --- /dev/null +++ b/image-scanner/grafeas/api/swagger.yaml @@ -0,0 +1,6091 @@ +--- +swagger: "2.0" +info: + version: "version not set" + title: "grafeas.proto" +schemes: +- "http" +- "https" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /v1beta1/{name=projects/*/notes/*}: + get: + tags: + - "GrafeasV1Beta1" + summary: "Gets the specified note." + operationId: "GetNote" + parameters: + - name: "name" + in: "path" + description: "The name of the note in the form of\n`projects/[PROVIDER_ID]/notes/[NOTE_ID]`." + required: true + type: "string" + x-exportParamName: "Name" + responses: + 200: + description: "A successful response." + schema: + $ref: "#/definitions/v1beta1Note" + delete: + tags: + - "GrafeasV1Beta1" + summary: "Deletes the specified note." + operationId: "DeleteNote" + parameters: + - name: "name" + in: "path" + description: "The name of the note in the form of\n`projects/[PROVIDER_ID]/notes/[NOTE_ID]`." + required: true + type: "string" + x-exportParamName: "Name" + responses: + 200: + description: "A successful response." + schema: {} + patch: + tags: + - "GrafeasV1Beta1" + summary: "Updates the specified note." + operationId: "UpdateNote" + parameters: + - name: "name" + in: "path" + description: "The name of the note in the form of\n`projects/[PROVIDER_ID]/notes/[NOTE_ID]`." + required: true + type: "string" + x-exportParamName: "Name" + - in: "body" + name: "body" + description: "The updated note." + required: true + schema: + $ref: "#/definitions/v1beta1Note" + x-exportParamName: "Body" + responses: + 200: + description: "A successful response." + schema: + $ref: "#/definitions/v1beta1Note" + /v1beta1/{name=projects/*/notes/*}/occurrences: + get: + tags: + - "GrafeasV1Beta1" + summary: "Lists occurrences referencing the specified note. Provider projects\ + \ can use\nthis method to get all occurrences across consumer projects referencing\ + \ the\nspecified note." + operationId: "ListNoteOccurrences" + parameters: + - name: "name" + in: "path" + description: "The name of the note to list occurrences for in the form of\n\ + `projects/[PROVIDER_ID]/notes/[NOTE_ID]`." + required: true + type: "string" + x-exportParamName: "Name" + - name: "filter" + in: "query" + description: "The filter expression." + required: false + type: "string" + x-exportParamName: "Filter" + x-optionalDataType: "String" + - name: "page_size" + in: "query" + description: "Number of occurrences to return in the list." + required: false + type: "integer" + format: "int32" + x-exportParamName: "PageSize" + x-optionalDataType: "Int32" + - name: "page_token" + in: "query" + description: "Token to provide to skip to a particular spot in the list." + required: false + type: "string" + x-exportParamName: "PageToken" + x-optionalDataType: "String" + responses: + 200: + description: "A successful response." + schema: + $ref: "#/definitions/v1beta1ListNoteOccurrencesResponse" + /v1beta1/{name=projects/*/occurrences/*}: + get: + tags: + - "GrafeasV1Beta1" + summary: "Gets the specified occurrence." + operationId: "GetOccurrence" + parameters: + - name: "name" + in: "path" + description: "The name of the occurrence in the form of\n`projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`." + required: true + type: "string" + x-exportParamName: "Name" + responses: + 200: + description: "A successful response." + schema: + $ref: "#/definitions/v1beta1Occurrence" + delete: + tags: + - "GrafeasV1Beta1" + summary: "Deletes the specified occurrence. For example, use this method to\ + \ delete an\noccurrence when the occurrence is no longer applicable for the\ + \ given\nresource." + operationId: "DeleteOccurrence" + parameters: + - name: "name" + in: "path" + description: "The name of the occurrence in the form of\n`projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`." + required: true + type: "string" + x-exportParamName: "Name" + responses: + 200: + description: "A successful response." + schema: {} + patch: + tags: + - "GrafeasV1Beta1" + summary: "Updates the specified occurrence." + operationId: "UpdateOccurrence" + parameters: + - name: "name" + in: "path" + description: "The name of the occurrence in the form of\n`projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`." + required: true + type: "string" + x-exportParamName: "Name" + - in: "body" + name: "body" + description: "The updated occurrence." + required: true + schema: + $ref: "#/definitions/v1beta1Occurrence" + x-exportParamName: "Body" + responses: + 200: + description: "A successful response." + schema: + $ref: "#/definitions/v1beta1Occurrence" + /v1beta1/{name=projects/*/occurrences/*}/notes: + get: + tags: + - "GrafeasV1Beta1" + summary: "Gets the note attached to the specified occurrence. Consumer projects\ + \ can\nuse this method to get a note that belongs to a provider project." + operationId: "GetOccurrenceNote" + parameters: + - name: "name" + in: "path" + description: "The name of the occurrence in the form of\n`projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`." + required: true + type: "string" + x-exportParamName: "Name" + responses: + 200: + description: "A successful response." + schema: + $ref: "#/definitions/v1beta1Note" + /v1beta1/{parent=projects/*}/notes: + get: + tags: + - "GrafeasV1Beta1" + summary: "Lists notes for the specified project." + operationId: "ListNotes" + parameters: + - name: "parent" + in: "path" + description: "The name of the project to list notes for in the form of\n`projects/[PROJECT_ID]`." + required: true + type: "string" + x-exportParamName: "Parent" + - name: "filter" + in: "query" + description: "The filter expression." + required: false + type: "string" + x-exportParamName: "Filter" + x-optionalDataType: "String" + - name: "page_size" + in: "query" + description: "Number of notes to return in the list. Must be positive. Max\ + \ allowed page\nsize is 1000. If not specified, page size defaults to 20." + required: false + type: "integer" + format: "int32" + x-exportParamName: "PageSize" + x-optionalDataType: "Int32" + - name: "page_token" + in: "query" + description: "Token to provide to skip to a particular spot in the list." + required: false + type: "string" + x-exportParamName: "PageToken" + x-optionalDataType: "String" + responses: + 200: + description: "A successful response." + schema: + $ref: "#/definitions/v1beta1ListNotesResponse" + post: + tags: + - "GrafeasV1Beta1" + summary: "Creates a new note." + operationId: "CreateNote" + parameters: + - name: "parent" + in: "path" + description: "The name of the project in the form of `projects/[PROJECT_ID]`,\ + \ under which\nthe note is to be created." + required: true + type: "string" + x-exportParamName: "Parent" + - in: "body" + name: "body" + description: "The note to create." + required: true + schema: + $ref: "#/definitions/v1beta1Note" + x-exportParamName: "Body" + responses: + 200: + description: "A successful response." + schema: + $ref: "#/definitions/v1beta1Note" + /v1beta1/{parent=projects/*}/notes:batchCreate: + post: + tags: + - "GrafeasV1Beta1" + summary: "Creates new notes in batch." + operationId: "BatchCreateNotes" + parameters: + - name: "parent" + in: "path" + description: "The name of the project in the form of `projects/[PROJECT_ID]`,\ + \ under which\nthe notes are to be created." + required: true + type: "string" + x-exportParamName: "Parent" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/v1beta1BatchCreateNotesRequest" + x-exportParamName: "Body" + responses: + 200: + description: "A successful response." + schema: + $ref: "#/definitions/v1beta1BatchCreateNotesResponse" + /v1beta1/{parent=projects/*}/occurrences: + get: + tags: + - "GrafeasV1Beta1" + summary: "Lists occurrences for the specified project." + operationId: "ListOccurrences" + parameters: + - name: "parent" + in: "path" + description: "The name of the project to list occurrences for in the form\ + \ of\n`projects/[PROJECT_ID]`." + required: true + type: "string" + x-exportParamName: "Parent" + - name: "filter" + in: "query" + description: "The filter expression." + required: false + type: "string" + x-exportParamName: "Filter" + x-optionalDataType: "String" + - name: "page_size" + in: "query" + description: "Number of occurrences to return in the list. Must be positive.\ + \ Max allowed\npage size is 1000. If not specified, page size defaults to\ + \ 20." + required: false + type: "integer" + format: "int32" + x-exportParamName: "PageSize" + x-optionalDataType: "Int32" + - name: "page_token" + in: "query" + description: "Token to provide to skip to a particular spot in the list." + required: false + type: "string" + x-exportParamName: "PageToken" + x-optionalDataType: "String" + responses: + 200: + description: "A successful response." + schema: + $ref: "#/definitions/v1beta1ListOccurrencesResponse" + post: + tags: + - "GrafeasV1Beta1" + summary: "Creates a new occurrence." + operationId: "CreateOccurrence" + parameters: + - name: "parent" + in: "path" + description: "The name of the project in the form of `projects/[PROJECT_ID]`,\ + \ under which\nthe occurrence is to be created." + required: true + type: "string" + x-exportParamName: "Parent" + - in: "body" + name: "body" + description: "The occurrence to create." + required: true + schema: + $ref: "#/definitions/v1beta1Occurrence" + x-exportParamName: "Body" + responses: + 200: + description: "A successful response." + schema: + $ref: "#/definitions/v1beta1Occurrence" + /v1beta1/{parent=projects/*}/occurrences:batchCreate: + post: + tags: + - "GrafeasV1Beta1" + summary: "Creates new occurrences in batch." + operationId: "BatchCreateOccurrences" + parameters: + - name: "parent" + in: "path" + description: "The name of the project in the form of `projects/[PROJECT_ID]`,\ + \ under which\nthe occurrences are to be created." + required: true + type: "string" + x-exportParamName: "Parent" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/v1beta1BatchCreateOccurrencesRequest" + x-exportParamName: "Body" + responses: + 200: + description: "A successful response." + schema: + $ref: "#/definitions/v1beta1BatchCreateOccurrencesResponse" + /v1beta1/{parent=projects/*}/occurrences:vulnerabilitySummary: + get: + tags: + - "GrafeasV1Beta1" + summary: "Gets a summary of the number and severity of occurrences." + operationId: "GetVulnerabilityOccurrencesSummary" + parameters: + - name: "parent" + in: "path" + description: "The name of the project to get a vulnerability summary for in\ + \ the form of\n`projects/[PROJECT_ID]`." + required: true + type: "string" + x-exportParamName: "Parent" + - name: "filter" + in: "query" + description: "The filter expression." + required: false + type: "string" + x-exportParamName: "Filter" + x-optionalDataType: "String" + responses: + 200: + description: "A successful response." + schema: + $ref: "#/definitions/v1beta1VulnerabilityOccurrencesSummary" +definitions: + AliasContextKind: + type: "string" + description: "The type of an alias.\n\n - KIND_UNSPECIFIED: Unknown.\n - FIXED:\ + \ Git tag.\n - MOVABLE: Git branch.\n - OTHER: Used to specify non-standard\ + \ aliases. For example, if a Git repo has a\nref named \"refs/foo/bar\"." + enum: + - "KIND_UNSPECIFIED" + - "FIXED" + - "MOVABLE" + - "OTHER" + default: "KIND_UNSPECIFIED" + AuthorityHint: + type: "object" + properties: + human_readable_name: + type: "string" + description: "Required. The human readable name of this attestation authority,\ + \ for\nexample \"qa\"." + description: "This submessage provides human-readable hints about the purpose\ + \ of the\nauthority. Because the name of a note acts as its resource reference,\ + \ it is\nimportant to disambiguate the canonical name of the Note (which might\ + \ be a\nUUID for security purposes) from \"readable\" names more suitable for\ + \ debug\noutput. Note that these hints should not be used to look up authorities\ + \ in\nsecurity sensitive contexts, such as when looking up attestations to\n\ + verify." + example: + human_readable_name: "human_readable_name" + BuildSignatureKeyType: + type: "string" + description: "Public key formats.\n\n - KEY_TYPE_UNSPECIFIED: `KeyType` is not\ + \ set.\n - PGP_ASCII_ARMORED: `PGP ASCII Armored` public key.\n - PKIX_PEM:\ + \ `PKIX PEM` public key." + enum: + - "KEY_TYPE_UNSPECIFIED" + - "PGP_ASCII_ARMORED" + - "PKIX_PEM" + default: "KEY_TYPE_UNSPECIFIED" + CVSSv3AttackComplexity: + type: "string" + enum: + - "ATTACK_COMPLEXITY_UNSPECIFIED" + - "ATTACK_COMPLEXITY_LOW" + - "ATTACK_COMPLEXITY_HIGH" + default: "ATTACK_COMPLEXITY_UNSPECIFIED" + CVSSv3AttackVector: + type: "string" + enum: + - "ATTACK_VECTOR_UNSPECIFIED" + - "ATTACK_VECTOR_NETWORK" + - "ATTACK_VECTOR_ADJACENT" + - "ATTACK_VECTOR_LOCAL" + - "ATTACK_VECTOR_PHYSICAL" + default: "ATTACK_VECTOR_UNSPECIFIED" + CVSSv3Impact: + type: "string" + enum: + - "IMPACT_UNSPECIFIED" + - "IMPACT_HIGH" + - "IMPACT_LOW" + - "IMPACT_NONE" + default: "IMPACT_UNSPECIFIED" + CVSSv3PrivilegesRequired: + type: "string" + enum: + - "PRIVILEGES_REQUIRED_UNSPECIFIED" + - "PRIVILEGES_REQUIRED_NONE" + - "PRIVILEGES_REQUIRED_LOW" + - "PRIVILEGES_REQUIRED_HIGH" + default: "PRIVILEGES_REQUIRED_UNSPECIFIED" + CVSSv3Scope: + type: "string" + enum: + - "SCOPE_UNSPECIFIED" + - "SCOPE_UNCHANGED" + - "SCOPE_CHANGED" + default: "SCOPE_UNSPECIFIED" + CVSSv3UserInteraction: + type: "string" + enum: + - "USER_INTERACTION_UNSPECIFIED" + - "USER_INTERACTION_NONE" + - "USER_INTERACTION_REQUIRED" + default: "USER_INTERACTION_UNSPECIFIED" + DeploymentPlatform: + type: "string" + description: "Types of platforms.\n\n - PLATFORM_UNSPECIFIED: Unknown.\n - GKE:\ + \ Google Container Engine.\n - FLEX: Google App Engine: Flexible Environment.\n\ + \ - CUSTOM: Custom user-defined platform." + enum: + - "PLATFORM_UNSPECIFIED" + - "GKE" + - "FLEX" + - "CUSTOM" + default: "PLATFORM_UNSPECIFIED" + DiscoveredAnalysisStatus: + type: "string" + description: "Analysis status for a resource. Currently for initial analysis only\ + \ (not\nupdated in continuous analysis).\n\n - ANALYSIS_STATUS_UNSPECIFIED:\ + \ Unknown.\n - PENDING: Resource is known but no action has been taken yet.\n\ + \ - SCANNING: Resource is being analyzed.\n - FINISHED_SUCCESS: Analysis has\ + \ finished successfully.\n - FINISHED_FAILED: Analysis has finished unsuccessfully,\ + \ the analysis itself is in a bad\nstate.\n - FINISHED_UNSUPPORTED: The resource\ + \ is known not to be supported" + enum: + - "ANALYSIS_STATUS_UNSPECIFIED" + - "PENDING" + - "SCANNING" + - "FINISHED_SUCCESS" + - "FINISHED_FAILED" + - "FINISHED_UNSUPPORTED" + default: "ANALYSIS_STATUS_UNSPECIFIED" + DiscoveredContinuousAnalysis: + type: "string" + description: "Whether the resource is continuously analyzed.\n\n - CONTINUOUS_ANALYSIS_UNSPECIFIED:\ + \ Unknown.\n - ACTIVE: The resource is continuously analyzed.\n - INACTIVE:\ + \ The resource is ignored for continuous analysis." + enum: + - "CONTINUOUS_ANALYSIS_UNSPECIFIED" + - "ACTIVE" + - "INACTIVE" + default: "CONTINUOUS_ANALYSIS_UNSPECIFIED" + HashHashType: + type: "string" + description: "Specifies the hash algorithm.\n\n - HASH_TYPE_UNSPECIFIED: Unknown.\n\ + \ - SHA256: A SHA-256 hash." + enum: + - "HASH_TYPE_UNSPECIFIED" + - "SHA256" + default: "HASH_TYPE_UNSPECIFIED" + InTotoArtifactRule: + type: "object" + properties: + artifact_rule: + type: "array" + items: + type: "string" + title: "Defines an object to declare an in-toto artifact rule" + example: + artifact_rule: + - "artifact_rule" + - "artifact_rule" + LayerDirective: + type: "string" + description: "Instructions from Dockerfile.\n\n - DIRECTIVE_UNSPECIFIED: Default\ + \ value for unsupported/missing directive.\n - MAINTAINER: https://docs.docker.com/engine/reference/builder/\n\ + \ - RUN: https://docs.docker.com/engine/reference/builder/\n - CMD: https://docs.docker.com/engine/reference/builder/\n\ + \ - LABEL: https://docs.docker.com/engine/reference/builder/\n - EXPOSE: https://docs.docker.com/engine/reference/builder/\n\ + \ - ENV: https://docs.docker.com/engine/reference/builder/\n - ADD: https://docs.docker.com/engine/reference/builder/\n\ + \ - COPY: https://docs.docker.com/engine/reference/builder/\n - ENTRYPOINT:\ + \ https://docs.docker.com/engine/reference/builder/\n - VOLUME: https://docs.docker.com/engine/reference/builder/\n\ + \ - USER: https://docs.docker.com/engine/reference/builder/\n - WORKDIR: https://docs.docker.com/engine/reference/builder/\n\ + \ - ARG: https://docs.docker.com/engine/reference/builder/\n - ONBUILD: https://docs.docker.com/engine/reference/builder/\n\ + \ - STOPSIGNAL: https://docs.docker.com/engine/reference/builder/\n - HEALTHCHECK:\ + \ https://docs.docker.com/engine/reference/builder/\n - SHELL: https://docs.docker.com/engine/reference/builder/" + enum: + - "DIRECTIVE_UNSPECIFIED" + - "MAINTAINER" + - "RUN" + - "CMD" + - "LABEL" + - "EXPOSE" + - "ENV" + - "ADD" + - "COPY" + - "ENTRYPOINT" + - "VOLUME" + - "USER" + - "WORKDIR" + - "ARG" + - "ONBUILD" + - "STOPSIGNAL" + - "HEALTHCHECK" + - "SHELL" + default: "DIRECTIVE_UNSPECIFIED" + LinkArtifactHashes: + type: "object" + properties: + sha256: + type: "string" + description: "Defines a hash object for use in Materials and Products." + example: + sha256: "sha256" + LinkByProducts: + type: "object" + properties: + custom_values: + type: "object" + additionalProperties: + type: "string" + description: "Defines an object for the byproducts field in in-toto links. The\ + \ suggested\nfields are \"stderr\", \"stdout\", and \"return-value\"." + example: + custom_values: + key: "custom_values" + LinkEnvironment: + type: "object" + properties: + custom_values: + type: "object" + additionalProperties: + type: "string" + description: "Defines an object for the environment field in in-toto links. The\ + \ suggested\nfields are \"variables\", \"filesystem\", and \"workdir\"." + example: + custom_values: + key: "custom_values" + VersionVersionKind: + type: "string" + description: "Whether this is an ordinary package version or a sentinel MIN/MAX\ + \ version.\n\n - VERSION_KIND_UNSPECIFIED: Unknown.\n - NORMAL: A standard package\ + \ version.\n - MINIMUM: A special version representing negative infinity.\n\ + \ - MAXIMUM: A special version representing positive infinity." + enum: + - "VERSION_KIND_UNSPECIFIED" + - "NORMAL" + - "MINIMUM" + - "MAXIMUM" + default: "VERSION_KIND_UNSPECIFIED" + VulnerabilityDetail: + type: "object" + properties: + cpe_uri: + type: "string" + description: "Required. The CPE URI in\n[cpe format](https://cpe.mitre.org/specification/)\ + \ in which the\nvulnerability manifests. Examples include distro or storage\ + \ location for\nvulnerable jar." + package: + type: "string" + description: "Required. The name of the package where the vulnerability was\ + \ found." + min_affected_version: + description: "The min version of the package in which the vulnerability exists." + $ref: "#/definitions/packageVersion" + max_affected_version: + description: "The max version of the package in which the vulnerability exists." + $ref: "#/definitions/packageVersion" + severity_name: + type: "string" + description: "The severity (eg: distro assigned severity) for this vulnerability." + description: + type: "string" + description: "A vendor-specific description of this note." + fixed_location: + description: "The fix for this specific package version." + $ref: "#/definitions/vulnerabilityVulnerabilityLocation" + package_type: + type: "string" + description: "The type of package; whether native or non native(ruby gems,\ + \ node.js\npackages etc)." + is_obsolete: + type: "boolean" + format: "boolean" + description: "Whether this detail is obsolete. Occurrences are expected not\ + \ to point to\nobsolete details." + source_update_time: + type: "string" + format: "date-time" + description: "The time this information was last changed at the source. This\ + \ is an\nupstream timestamp from the underlying information source - e.g.\ + \ Ubuntu\nsecurity tracker." + title: "Identifies all appearances of this vulnerability in the package for a\n\ + specific distro/location. For example: glibc in\ncpe:/o:debian:debian_linux:8\ + \ for versions 2.1 - 2.2" + example: + fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + package: "package" + cpe_uri: "cpe_uri" + max_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + description: "description" + min_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + source_update_time: "2000-01-23T04:56:07.000+00:00" + package_type: "package_type" + is_obsolete: true + VulnerabilityOccurrencesSummaryFixableTotalByDigest: + type: "object" + properties: + resource: + description: "The affected resource." + $ref: "#/definitions/v1beta1Resource" + severity: + description: "The severity for this count. SEVERITY_UNSPECIFIED indicates\ + \ total across\nall severities." + $ref: "#/definitions/vulnerabilitySeverity" + fixable_count: + type: "string" + format: "int64" + description: "The number of fixable vulnerabilities associated with this resource." + total_count: + type: "string" + format: "int64" + description: "The total number of vulnerabilities associated with this resource." + description: "Per resource and severity counts of fixable and total vulnerabilities." + example: + severity: {} + resource: + name: "name" + content_hash: + type: {} + value: "value" + uri: "uri" + total_count: "total_count" + fixable_count: "fixable_count" + VulnerabilityWindowsDetail: + type: "object" + properties: + cpe_uri: + type: "string" + description: "Required. The CPE URI in\n[cpe format](https://cpe.mitre.org/specification/)\ + \ in which the\nvulnerability manifests. Examples include distro or storage\ + \ location for\nvulnerable jar." + name: + type: "string" + description: "Required. The name of the vulnerability." + description: + type: "string" + description: "The description of the vulnerability." + fixing_kbs: + type: "array" + description: "Required. The names of the KBs which have hotfixes to mitigate\ + \ this\nvulnerability. Note that there may be multiple hotfixes (and thus\n\ + multiple KBs) that mitigate a given vulnerability. Currently any listed\n\ + kb's presence is considered a fix." + items: + $ref: "#/definitions/WindowsDetailKnowledgeBase" + example: + cpe_uri: "cpe_uri" + fixing_kbs: + - name: "name" + url: "url" + - name: "name" + url: "url" + name: "name" + description: "description" + WindowsDetailKnowledgeBase: + type: "object" + properties: + name: + type: "string" + description: "The KB name (generally of the form KB[0-9]+ i.e. KB123456)." + url: + type: "string" + title: "A link to the KB in the Windows update catalog -\nhttps://www.catalog.update.microsoft.com/" + example: + name: "name" + url: "url" + attestationAttestation: + type: "object" + properties: + pgp_signed_attestation: + description: "A PGP signed attestation." + $ref: "#/definitions/attestationPgpSignedAttestation" + generic_signed_attestation: + description: "An attestation that supports multiple `Signature`s\nover the\ + \ same attestation payload. The signatures\n(defined in common.proto) support\ + \ a superset of\npublic key types and IDs compared to PgpSignedAttestation." + $ref: "#/definitions/attestationGenericSignedAttestation" + description: "Occurrence that represents a single \"attestation\". The authenticity\ + \ of an\nattestation can be verified using the attached signature. If the verifier\n\ + trusts the public key of the signer, then verifying the signature is\nsufficient\ + \ to establish trust. In this circumstance, the authority to which\nthis attestation\ + \ is attached is primarily useful for look-up (how to find\nthis attestation\ + \ if you already know the authority and artifact to be\nverified) and intent\ + \ (which authority was this attestation intended to sign\nfor)." + example: + pgp_signed_attestation: + content_type: {} + signature: "signature" + pgp_key_id: "pgp_key_id" + generic_signed_attestation: + content_type: {} + serialized_payload: "serialized_payload" + signatures: + - signature: "signature" + public_key_id: "public_key_id" + - signature: "signature" + public_key_id: "public_key_id" + attestationAuthority: + type: "object" + properties: + hint: + description: "Hint hints at the purpose of the attestation authority." + $ref: "#/definitions/AuthorityHint" + description: "Note kind that represents a logical attestation \"role\" or \"authority\"\ + . For\nexample, an organization might have one `Authority` for \"QA\" and one\ + \ for\n\"build\". This note is intended to act strictly as a grouping mechanism\ + \ for\nthe attached occurrences (Attestations). This grouping mechanism also\n\ + provides a security boundary, since IAM ACLs gate the ability for a principle\n\ + to attach an occurrence to a given note. It also provides a single point of\n\ + lookup to find all attached attestation occurrences, even if they don't all\n\ + live in the same project." + example: + hint: + human_readable_name: "human_readable_name" + attestationGenericSignedAttestation: + type: "object" + properties: + content_type: + description: "Type (for example schema) of the attestation payload that was\ + \ signed.\nThe verifier must ensure that the provided type is one that the\ + \ verifier\nsupports, and that the attestation payload is a valid instantiation\ + \ of that\ntype (for example by validating a JSON schema)." + $ref: "#/definitions/attestationGenericSignedAttestationContentType" + serialized_payload: + type: "string" + format: "byte" + description: "The serialized payload that is verified by one or more `signatures`.\n\ + The encoding and semantic meaning of this payload must match what is set\ + \ in\n`content_type`." + pattern: "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$" + signatures: + type: "array" + description: "One or more signatures over `serialized_payload`. Verifier\ + \ implementations\nshould consider this attestation message verified if\ + \ at least one\n`signature` verifies `serialized_payload`. See `Signature`\ + \ in common.proto\nfor more details on signature structure and verification." + items: + $ref: "#/definitions/grafeasv1beta1Signature" + description: "An attestation wrapper that uses the Grafeas `Signature` message.\n\ + This attestation must define the `serialized_payload` that the `signatures`\ + \ verify\nand any metadata necessary to interpret that plaintext. The signatures\n\ + should always be over the `serialized_payload` bytestring." + example: + content_type: {} + serialized_payload: "serialized_payload" + signatures: + - signature: "signature" + public_key_id: "public_key_id" + - signature: "signature" + public_key_id: "public_key_id" + attestationGenericSignedAttestationContentType: + type: "string" + description: "Type of the attestation plaintext that was signed.\n\n - CONTENT_TYPE_UNSPECIFIED:\ + \ `ContentType` is not set.\n - SIMPLE_SIGNING_JSON: Atomic format attestation\ + \ signature. See\nhttps://github.com/containers/image/blob/8a5d2f82a6e3263290c8e0276c3e0f64e77723e7/docs/atomic-signature.md\n\ + The payload extracted in `plaintext` is a JSON blob conforming to the\nlinked\ + \ schema." + enum: + - "CONTENT_TYPE_UNSPECIFIED" + - "SIMPLE_SIGNING_JSON" + default: "CONTENT_TYPE_UNSPECIFIED" + attestationPgpSignedAttestation: + type: "object" + properties: + signature: + type: "string" + description: "Required. The raw content of the signature, as output by GNU\ + \ Privacy Guard\n(GPG) or equivalent. Since this message only supports attached\ + \ signatures,\nthe payload that was signed must be attached. While the signature\ + \ format\nsupported is dependent on the verification implementation, currently\ + \ only\nASCII-armored (`--armor` to gpg), non-clearsigned (`--sign` rather\ + \ than\n`--clearsign` to gpg) are supported. Concretely, `gpg --sign --armor\n\ + --output=signature.gpg payload.json` will create the signature content\n\ + expected in this field in `signature.gpg` for the `payload.json`\nattestation\ + \ payload." + content_type: + description: "Type (for example schema) of the attestation payload that was\ + \ signed.\nThe verifier must ensure that the provided type is one that the\ + \ verifier\nsupports, and that the attestation payload is a valid instantiation\ + \ of that\ntype (for example by validating a JSON schema)." + $ref: "#/definitions/attestationPgpSignedAttestationContentType" + pgp_key_id: + type: "string" + description: "The cryptographic fingerprint of the key used to generate the\ + \ signature,\nas output by, e.g. `gpg --list-keys`. This should be the version\ + \ 4, full\n160-bit fingerprint, expressed as a 40 character hexidecimal\ + \ string. See\nhttps://tools.ietf.org/html/rfc4880#section-12.2 for details.\n\ + Implementations may choose to acknowledge \"LONG\", \"SHORT\", or other\n\ + abbreviated key IDs, but only the full fingerprint is guaranteed to work.\n\ + In gpg, the full fingerprint can be retrieved from the `fpr` field\nreturned\ + \ when calling --list-keys with --with-colons. For example:\n```\ngpg --with-colons\ + \ --with-fingerprint --force-v4-certs \\\n --list-keys attester@example.com\n\ + tru::1:1513631572:0:3:1:5\npub:......\nfpr:::::::::24FF6481B76AC91E66A00AC657A93A81EF3AE6FB:\n\ + ```\nAbove, the fingerprint is `24FF6481B76AC91E66A00AC657A93A81EF3AE6FB`." + description: "An attestation wrapper with a PGP-compatible signature. This message\ + \ only\nsupports `ATTACHED` signatures, where the payload that is signed is\ + \ included\nalongside the signature itself in the same file." + example: + content_type: {} + signature: "signature" + pgp_key_id: "pgp_key_id" + attestationPgpSignedAttestationContentType: + type: "string" + description: "Type (for example schema) of the attestation payload that was signed.\n\ + \n - CONTENT_TYPE_UNSPECIFIED: `ContentType` is not set.\n - SIMPLE_SIGNING_JSON:\ + \ Atomic format attestation signature. See\nhttps://github.com/containers/image/blob/8a5d2f82a6e3263290c8e0276c3e0f64e77723e7/docs/atomic-signature.md\n\ + The payload extracted from `signature` is a JSON blob conforming to the\nlinked\ + \ schema." + enum: + - "CONTENT_TYPE_UNSPECIFIED" + - "SIMPLE_SIGNING_JSON" + default: "CONTENT_TYPE_UNSPECIFIED" + buildBuild: + type: "object" + properties: + builder_version: + type: "string" + description: "Required. Immutable. Version of the builder which produced this\ + \ build." + signature: + description: "Signature of the build in occurrences pointing to this build\ + \ note\ncontaining build details." + $ref: "#/definitions/buildBuildSignature" + description: "Note holding the version of the provider's builder and the signature\ + \ of the\nprovenance message in the build details occurrence." + example: + signature: + public_key: "public_key" + key_type: {} + signature: "signature" + key_id: "key_id" + builder_version: "builder_version" + buildBuildSignature: + type: "object" + properties: + public_key: + type: "string" + description: "Public key of the builder which can be used to verify that the\ + \ related\nfindings are valid and unchanged. If `key_type` is empty, this\ + \ defaults\nto PEM encoded public keys.\n\nThis field may be empty if `key_id`\ + \ references an external key.\n\nFor Cloud Build based signatures, this\ + \ is a PEM encoded public\nkey. To verify the Cloud Build signature, place\ + \ the contents of\nthis field into a file (public.pem). The signature field\ + \ is base64-decoded\ninto its binary representation in signature.bin, and\ + \ the provenance bytes\nfrom `BuildDetails` are base64-decoded into a binary\ + \ representation in\nsigned.bin. OpenSSL can then verify the signature:\n\ + `openssl sha256 -verify public.pem -signature signature.bin signed.bin`" + signature: + type: "string" + format: "byte" + description: "Required. Signature of the related `BuildProvenance`. In JSON,\ + \ this is\nbase-64 encoded." + pattern: "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$" + key_id: + type: "string" + description: "An ID for the key used to sign. This could be either an ID for\ + \ the key\nstored in `public_key` (such as the ID or fingerprint for a PGP\ + \ key, or the\nCN for a cert), or a reference to an external key (such as\ + \ a reference to a\nkey in Cloud Key Management Service)." + key_type: + description: "The type of the key, either stored in `public_key` or referenced\ + \ in\n`key_id`." + $ref: "#/definitions/BuildSignatureKeyType" + description: "Message encapsulating the signature of the verified build." + example: + public_key: "public_key" + key_type: {} + signature: "signature" + key_id: "key_id" + deploymentDeployable: + type: "object" + properties: + resource_uri: + type: "array" + description: "Required. Resource URI for the artifact being deployed." + items: + type: "string" + description: "An artifact that can be deployed in some runtime." + example: + resource_uri: + - "resource_uri" + - "resource_uri" + deploymentDeployment: + type: "object" + properties: + user_email: + type: "string" + description: "Identity of the user that triggered this deployment." + deploy_time: + type: "string" + format: "date-time" + description: "Required. Beginning of the lifetime of this deployment." + undeploy_time: + type: "string" + format: "date-time" + description: "End of the lifetime of this deployment." + config: + type: "string" + description: "Configuration used to create this deployment." + address: + type: "string" + description: "Address of the runtime element hosting this deployment." + resource_uri: + type: "array" + description: "Output only. Resource URI for the artifact being deployed taken\ + \ from\nthe deployable field with the same name." + readOnly: true + items: + type: "string" + platform: + description: "Platform hosting this deployment." + $ref: "#/definitions/DeploymentPlatform" + description: "The period during which some deployable was active in a runtime." + example: + user_email: "user_email" + address: "address" + resource_uri: + - "resource_uri" + - "resource_uri" + undeploy_time: "2000-01-23T04:56:07.000+00:00" + deploy_time: "2000-01-23T04:56:07.000+00:00" + config: "config" + platform: {} + discoveryDiscovered: + type: "object" + properties: + continuous_analysis: + description: "Whether the resource is continuously analyzed." + $ref: "#/definitions/DiscoveredContinuousAnalysis" + last_analysis_time: + type: "string" + format: "date-time" + description: "The last time continuous analysis was done for this resource.\n\ + Deprecated, do not use." + analysis_status: + description: "The status of discovery for the resource." + $ref: "#/definitions/DiscoveredAnalysisStatus" + analysis_status_error: + description: "When an error is encountered this will contain a LocalizedMessage\ + \ under\ndetails to show to the user. The LocalizedMessage is output only\ + \ and\npopulated by the API." + $ref: "#/definitions/rpcStatus" + description: "Provides information about the analysis status of a discovered resource." + example: + last_analysis_time: "2000-01-23T04:56:07.000+00:00" + analysis_status: {} + continuous_analysis: {} + analysis_status_error: + code: 1 + details: + - value: "value" + type_url: "type_url" + - value: "value" + type_url: "type_url" + message: "message" + discoveryDiscovery: + type: "object" + properties: + analysis_kind: + description: "Required. Immutable. The kind of analysis that is handled by\ + \ this\ndiscovery." + $ref: "#/definitions/v1beta1NoteKind" + description: "A note that indicates a type of analysis a provider would perform.\ + \ This note\nexists in a provider's project. A `Discovery` occurrence is created\ + \ in a\nconsumer's project at the start of analysis." + example: {} + grafeasv1beta1Signature: + type: "object" + properties: + signature: + type: "string" + format: "byte" + description: "The content of the signature, an opaque bytestring.\nThe payload\ + \ that this signature verifies MUST be unambiguously provided\nwith the\ + \ Signature during verification. A wrapper message might provide\nthe payload\ + \ explicitly. Alternatively, a message might have a canonical\nserialization\ + \ that can always be unambiguously computed to derive the\npayload." + pattern: "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$" + public_key_id: + type: "string" + description: "The identifier for the public key that verifies this signature.\n\ + \ * The `public_key_id` is required.\n * The `public_key_id` SHOULD be\ + \ an RFC3986 conformant URI.\n * When possible, the `public_key_id` SHOULD\ + \ be an immutable reference,\n such as a cryptographic digest.\n\nExamples\ + \ of valid `public_key_id`s:\n\nOpenPGP V4 public key fingerprint:\n *\ + \ \"openpgp4fpr:74FAF3B861BDA0870C7B6DEF607E48D2A663AEEA\"\nSee https://www.iana.org/assignments/uri-schemes/prov/openpgp4fpr\ + \ for more\ndetails on this scheme.\n\nRFC6920 digest-named SubjectPublicKeyInfo\ + \ (digest of the DER\nserialization):\n * \"ni:///sha-256;cD9o9Cq6LG3jD0iKXqEi_vdjJGecm_iXkbqVoScViaU\"\ + \n * \"nih:///sha-256;703f68f42aba2c6de30f488a5ea122fef76324679c9bf89791ba95a1271589a5\"" + description: "Verifiers (e.g. Kritis implementations) MUST verify signatures\n\ + with respect to the trust anchors defined in policy (e.g. a Kritis policy).\n\ + Typically this means that the verifier has been configured with a map from\n\ + `public_key_id` to public key material (and any required parameters, e.g.\n\ + signing algorithm).\n\nIn particular, verification implementations MUST NOT\ + \ treat the signature\n`public_key_id` as anything more than a key lookup hint.\ + \ The `public_key_id`\nDOES NOT validate or authenticate a public key; it only\ + \ provides a mechanism\nfor quickly selecting a public key ALREADY CONFIGURED\ + \ on the verifier through\na trusted channel. Verification implementations MUST\ + \ reject signatures in any\nof the following circumstances:\n * The `public_key_id`\ + \ is not recognized by the verifier.\n * The public key that `public_key_id`\ + \ refers to does not verify the\n signature with respect to the payload.\n\ + \nThe `signature` contents SHOULD NOT be \"attached\" (where the payload is\n\ + included with the serialized `signature` bytes). Verifiers MUST ignore any\n\ + \"attached\" payload and only verify signatures with respect to explicitly\n\ + provided payload (e.g. a `payload` field on the proto message that holds\nthis\ + \ Signature, or the canonical serialization of the proto message that\nholds\ + \ this signature)." + example: + signature: "signature" + public_key_id: "public_key_id" + imageBasis: + type: "object" + properties: + resource_url: + type: "string" + description: "Required. Immutable. The resource_url for the resource representing\ + \ the\nbasis of associated occurrence images." + fingerprint: + description: "Required. Immutable. The fingerprint of the base image." + $ref: "#/definitions/imageFingerprint" + description: "Basis describes the base image portion (Note) of the DockerImage\n\ + relationship. Linked occurrences are derived from this or an\nequivalent image\ + \ via:\n FROM \nOr an equivalent reference, e.g. a tag\ + \ of the resource_url." + example: + resource_url: "resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + imageDerived: + type: "object" + properties: + fingerprint: + description: "Required. The fingerprint of the derived image." + $ref: "#/definitions/imageFingerprint" + distance: + type: "integer" + format: "int32" + description: "Output only. The number of layers by which this image differs\ + \ from the\nassociated image basis." + readOnly: true + layer_info: + type: "array" + description: "This contains layer-specific metadata, if populated it has length\n\ + \"distance\" and is ordered with [distance] being the layer immediately\n\ + following the base image and [1] being the final layer." + items: + $ref: "#/definitions/imageLayer" + base_resource_url: + type: "string" + description: "Output only. This contains the base image URL for the derived\ + \ image\noccurrence." + readOnly: true + description: "Derived describes the derived image portion (Occurrence) of the\ + \ DockerImage\nrelationship. This image would be produced from a Dockerfile\ + \ with FROM\n." + example: + distance: 6 + base_resource_url: "base_resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + layer_info: + - arguments: "arguments" + directive: {} + - arguments: "arguments" + directive: {} + imageFingerprint: + type: "object" + properties: + v1_name: + type: "string" + description: "Required. The layer ID of the final layer in the Docker image's\ + \ v1\nrepresentation." + v2_blob: + type: "array" + description: "Required. The ordered list of v2 blobs that represent a given\ + \ image." + items: + type: "string" + v2_name: + type: "string" + description: "Output only. The name of the image's v2 blobs computed via:\n\ + \ [bottom] := v2_blob[bottom]\n [N] := sha256(v2_blob[N] + \" \" + v2_name[N+1])\n\ + Only the name of the final blob is kept." + readOnly: true + description: "A set of properties that uniquely identify a given Docker image." + example: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + imageLayer: + type: "object" + properties: + directive: + description: "Required. The recovered Dockerfile directive used to construct\ + \ this layer." + $ref: "#/definitions/LayerDirective" + arguments: + type: "string" + description: "The recovered arguments to the Dockerfile directive." + description: "Layer holds metadata specific to a layer of a Docker image." + example: + arguments: "arguments" + directive: {} + intotoInToto: + type: "object" + properties: + step_name: + type: "string" + description: "This field identifies the name of the step in the supply chain." + signing_keys: + type: "array" + description: "This field contains the public keys that can be used to verify\ + \ the\nsignatures on the step metadata." + items: + $ref: "#/definitions/intotoSigningKey" + expected_materials: + type: "array" + description: "The following fields contain in-toto artifact rules identifying\ + \ the\nartifacts that enter this supply chain step, and exit the supply\ + \ chain\nstep, i.e. materials and products of the step." + items: + $ref: "#/definitions/InTotoArtifactRule" + expected_products: + type: "array" + items: + $ref: "#/definitions/InTotoArtifactRule" + expected_command: + type: "array" + description: "This field contains the expected command used to perform the\ + \ step." + items: + type: "string" + threshold: + type: "string" + format: "int64" + description: "This field contains a value that indicates the minimum number\ + \ of keys that\nneed to be used to sign the step's in-toto link." + description: "This contains the fields corresponding to the definition of a software\ + \ supply\nchain step in an in-toto layout. This information goes into a Grafeas\ + \ note." + example: + expected_products: + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + step_name: "step_name" + signing_keys: + - key_type: "key_type" + key_scheme: "key_scheme" + key_id: "key_id" + public_key_value: "public_key_value" + - key_type: "key_type" + key_scheme: "key_scheme" + key_id: "key_id" + public_key_value: "public_key_value" + expected_command: + - "expected_command" + - "expected_command" + threshold: "threshold" + expected_materials: + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + intotoLink: + type: "object" + properties: + effective_command: + type: "array" + title: "This field contains the full command executed for the step. This can\ + \ also\nbe empty if links are generated for operations that aren't directly\ + \ mapped\nto a specific command. Each term in the command is an independent\ + \ string\nin the list. An example of a command in the in-toto metadata field\ + \ is:\n\"command\": [\"git\", \"clone\", \"https://github.com/in-toto/demo-project.git\"\ + ]" + items: + type: "string" + materials: + type: "array" + title: "Materials are the supply chain artifacts that go into the step and\ + \ are used\nfor the operation performed. The key of the map is the path\ + \ of the artifact\nand the structure contains the recorded hash information.\ + \ An example is:\n\"materials\": [\n {\n \"resource_uri\": \"foo/bar\"\ + ,\n \"hashes\": {\n \"sha256\": \"ebebf...\",\n : \n }\n }\n]" + items: + $ref: "#/definitions/intotoLinkArtifact" + products: + type: "array" + description: "Products are the supply chain artifacts generated as a result\ + \ of the step.\nThe structure is identical to that of materials." + items: + $ref: "#/definitions/intotoLinkArtifact" + byproducts: + description: "ByProducts are data generated as part of a software supply chain\ + \ step, but\nare not the actual result of the step." + $ref: "#/definitions/LinkByProducts" + environment: + title: "This is a field that can be used to capture information about the\n\ + environment. It is suggested for this field to contain information that\n\ + details environment variables, filesystem information, and the present\n\ + working directory. The recommended structure of this field is:\n\"environment\"\ + : {\n \"custom_values\": {\n \"variables\": \"\",\n \"filesystem\"\ + : \"\",\n \"workdir\": \"\",\n \"\"\ + : \"...\"\n }\n}" + $ref: "#/definitions/LinkEnvironment" + description: "This corresponds to an in-toto link." + example: + environment: + custom_values: + key: "custom_values" + effective_command: + - "effective_command" + - "effective_command" + materials: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + byproducts: + custom_values: + key: "custom_values" + products: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + intotoLinkArtifact: + type: "object" + properties: + resource_uri: + type: "string" + hashes: + $ref: "#/definitions/LinkArtifactHashes" + example: + resource_uri: "resource_uri" + hashes: + sha256: "sha256" + intotoSigningKey: + type: "object" + properties: + key_id: + type: "string" + description: "key_id is an identifier for the signing key." + key_type: + type: "string" + description: "This field identifies the specific signing method. Eg: \"rsa\"\ + , \"ed25519\",\nand \"ecdsa\"." + public_key_value: + type: "string" + description: "This field contains the actual public key." + key_scheme: + type: "string" + description: "This field contains the corresponding signature scheme.\nEg:\ + \ \"rsassa-pss-sha256\"." + description: "This defines the format used to record keys used in the software\ + \ supply\nchain. An in-toto link is attested using one or more keys defined\ + \ in the\nin-toto layout. An example of this is:\n{\n \"key_id\": \"776a00e29f3559e0141b3b096f696abc6cfb0c657ab40f441132b345b0...\"\ + ,\n \"key_type\": \"rsa\",\n \"public_key_value\": \"-----BEGIN PUBLIC KEY-----\\\ + nMIIBojANBgkqhkiG9w0B...\",\n \"key_scheme\": \"rsassa-pss-sha256\"\n}\nThe\ + \ format for in-toto's key definition can be found in section 4.2 of the\nin-toto\ + \ specification." + example: + key_type: "key_type" + key_scheme: "key_scheme" + key_id: "key_id" + public_key_value: "public_key_value" + packageArchitecture: + type: "string" + description: "Instruction set architectures supported by various package managers.\n\ + \n - ARCHITECTURE_UNSPECIFIED: Unknown architecture.\n - X86: X86 architecture.\n\ + \ - X64: X64 architecture." + enum: + - "ARCHITECTURE_UNSPECIFIED" + - "X86" + - "X64" + default: "ARCHITECTURE_UNSPECIFIED" + packageDistribution: + type: "object" + properties: + cpe_uri: + type: "string" + description: "Required. The cpe_uri in [CPE format](https://cpe.mitre.org/specification/)\n\ + denoting the package manager version distributing a package." + architecture: + description: "The CPU architecture for which packages in this distribution\ + \ channel were\nbuilt." + $ref: "#/definitions/packageArchitecture" + latest_version: + description: "The latest available version of this package in this distribution\ + \ channel." + $ref: "#/definitions/packageVersion" + maintainer: + type: "string" + description: "A freeform string denoting the maintainer of this package." + url: + type: "string" + description: "The distribution channel-specific homepage for this package." + description: + type: "string" + description: "The distribution channel-specific description of this package." + description: "This represents a particular channel of distribution for a given\ + \ package.\nE.g., Debian's jessie-backports dpkg mirror." + example: + latest_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + cpe_uri: "cpe_uri" + description: "description" + maintainer: "maintainer" + url: "url" + architecture: {} + packageInstallation: + type: "object" + properties: + name: + type: "string" + description: "Output only. The name of the installed package." + readOnly: true + location: + type: "array" + description: "Required. All of the places within the filesystem versions of\ + \ this package\nhave been found." + items: + $ref: "#/definitions/v1beta1packageLocation" + description: "This represents how a particular software package may be installed\ + \ on a\nsystem." + example: + name: "name" + location: + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + packagePackage: + type: "object" + properties: + name: + type: "string" + description: "Required. Immutable. The name of the package." + distribution: + type: "array" + description: "The various channels by which a package is distributed." + items: + $ref: "#/definitions/packageDistribution" + description: "This represents a particular package that is distributed over various\n\ + channels. E.g., glibc (aka libc6) is distributed by many, at various\nversions." + example: + name: "name" + distribution: + - latest_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + cpe_uri: "cpe_uri" + description: "description" + maintainer: "maintainer" + url: "url" + architecture: {} + - latest_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + cpe_uri: "cpe_uri" + description: "description" + maintainer: "maintainer" + url: "url" + architecture: {} + packageVersion: + type: "object" + properties: + epoch: + type: "integer" + format: "int32" + description: "Used to correct mistakes in the version numbering scheme." + name: + type: "string" + description: "Required only when version kind is NORMAL. The main part of\ + \ the version\nname." + revision: + type: "string" + description: "The iteration of the package build from the above version." + kind: + description: "Required. Distinguishes between sentinel MIN/MAX versions and\ + \ normal\nversions." + $ref: "#/definitions/VersionVersionKind" + description: "Version contains structured information about the version of a package." + example: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + protobufAny: + type: "object" + properties: + type_url: + type: "string" + description: "A URL/resource name that uniquely identifies the type of the\ + \ serialized\nprotocol buffer message. This string must contain at least\n\ + one \"/\" character. The last segment of the URL's path must represent\n\ + the fully qualified name of the type (as in\n`path/google.protobuf.Duration`).\ + \ The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\ + \nIn practice, teams usually precompile into the binary all types that they\n\ + expect it to use in the context of Any. However, for URLs which use the\n\ + scheme `http`, `https`, or no scheme, one can optionally set up a type\n\ + server that maps type URLs to message definitions as follows:\n\n* If no\ + \ scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must\ + \ yield a [google.protobuf.Type][]\n value in binary format, or produce\ + \ an error.\n* Applications are allowed to cache lookup results based on\ + \ the\n URL, or have them precompiled into a binary to avoid any\n lookup.\ + \ Therefore, binary compatibility needs to be preserved\n on changes to\ + \ types. (Use versioned type names to manage\n breaking changes.)\n\nNote:\ + \ this functionality is not currently available in the official\nprotobuf\ + \ release, and it is not used for type URLs beginning with\ntype.googleapis.com.\n\ + \nSchemes other than `http`, `https` (or the empty scheme) might be\nused\ + \ with implementation specific semantics." + value: + type: "string" + format: "byte" + description: "Must be a valid serialized protocol buffer of the above specified\ + \ type." + pattern: "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$" + description: "`Any` contains an arbitrary serialized protocol buffer message along\ + \ with a\nURL that describes the type of the serialized message.\n\nProtobuf\ + \ library provides support to pack/unpack Any values in the form\nof utility\ + \ functions or additional generated methods of the Any type.\n\nExample 1: Pack\ + \ and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n\ + \ ...\n if (any.UnpackTo(&foo)) {\n ...\n }\n\nExample 2: Pack\ + \ and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n\ + \ ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n\ + \ }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n\ + \ any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n\ + \ any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in\ + \ Go\n\n foo := &pb.Foo{...}\n any, err := ptypes.MarshalAny(foo)\n\ + \ ...\n foo := &pb.Foo{}\n if err := ptypes.UnmarshalAny(any, foo);\ + \ err != nil {\n ...\n }\n\nThe pack methods provided by protobuf\ + \ library will by default use\n'type.googleapis.com/full.type.name' as the type\ + \ URL and the unpack\nmethods only use the fully qualified type name after the\ + \ last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\n\ + name \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses\ + \ the regular\nrepresentation of the deserialized, embedded message, with an\n\ + additional field `@type` which contains the type URL. Example:\n\n package\ + \ google.profile;\n message Person {\n string first_name = 1;\n \ + \ string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\"\ + ,\n \"firstName\": ,\n \"lastName\": \n }\n\nIf\ + \ the embedded message type is well-known and has a custom JSON\nrepresentation,\ + \ that representation will be embedded adding a field\n`value` which holds the\ + \ custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\ + \n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n\ + \ \"value\": \"1.212s\"\n }" + example: + value: "value" + type_url: "type_url" + protobufFieldMask: + type: "object" + properties: + paths: + type: "array" + description: "The set of field mask paths." + items: + type: "string" + title: "`FieldMask` represents a set of symbolic field paths, for example:" + description: "paths: \"f.a\"\n paths: \"f.b.d\"\n\nHere `f` represents a field\ + \ in some root message, `a` and `b`\nfields in the message found in `f`, and\ + \ `d` a field found in the\nmessage in `f.b`.\n\nField masks are used to specify\ + \ a subset of fields that should be\nreturned by a get operation or modified\ + \ by an update operation.\nField masks also have a custom JSON encoding (see\ + \ below).\n\n# Field Masks in Projections\n\nWhen used in the context of a projection,\ + \ a response message or\nsub-message is filtered by the API to only contain\ + \ those fields as\nspecified in the mask. For example, if the mask in the previous\n\ + example is applied to a response message as follows:\n\n f {\n a : 22\n\ + \ b {\n d : 1\n x : 2\n }\n y : 13\n }\n \ + \ z: 8\n\nThe result will not contain specific values for fields x,y and z\n\ + (their value will be set to the default, and omitted in proto text\noutput):\n\ + \n\n f {\n a : 22\n b {\n d : 1\n }\n }\n\nA repeated\ + \ field is not allowed except at the last position of a\npaths string.\n\nIf\ + \ a FieldMask object is not present in a get operation, the\noperation applies\ + \ to all fields (as if a FieldMask of all fields\nhad been specified).\n\nNote\ + \ that a field mask does not necessarily apply to the\ntop-level response message.\ + \ In case of a REST get operation, the\nfield mask applies directly to the response,\ + \ but in case of a REST\nlist operation, the mask instead applies to each individual\ + \ message\nin the returned resource list. In case of a REST custom method,\n\ + other definitions may be used. Where the mask applies will be\nclearly documented\ + \ together with its declaration in the API. In\nany case, the effect on the\ + \ returned resource/resources is required\nbehavior for APIs.\n\n# Field Masks\ + \ in Update Operations\n\nA field mask in update operations specifies which\ + \ fields of the\ntargeted resource are going to be updated. The API is required\n\ + to only change the values of the fields as specified in the mask\nand leave\ + \ the others untouched. If a resource is passed in to\ndescribe the updated\ + \ values, the API ignores the values of all\nfields not covered by the mask.\n\ + \nIf a repeated field is specified for an update operation, new values will\n\ + be appended to the existing repeated field in the target resource. Note that\n\ + a repeated field is only allowed in the last position of a `paths` string.\n\ + \nIf a sub-message is specified in the last position of the field mask for an\n\ + update operation, then new value will be merged into the existing sub-message\n\ + in the target resource.\n\nFor example, given the target message:\n\n f {\n\ + \ b {\n d: 1\n x: 2\n }\n c: [1]\n }\n\nAnd\ + \ an update message:\n\n f {\n b {\n d: 10\n }\n c:\ + \ [2]\n }\n\nthen if the field mask is:\n\n paths: [\"f.b\", \"f.c\"]\n\n\ + then the result will be:\n\n f {\n b {\n d: 10\n x: 2\n\ + \ }\n c: [1, 2]\n }\n\nAn implementation may provide options to\ + \ override this default behavior for\nrepeated and message fields.\n\nIn order\ + \ to reset a field's value to the default, the field must\nbe in the mask and\ + \ set to the default value in the provided resource.\nHence, in order to reset\ + \ all fields of a resource, provide a default\ninstance of the resource and\ + \ set all fields in the mask, or do\nnot provide a mask as described below.\n\ + \nIf a field mask is not present on update, the operation applies to\nall fields\ + \ (as if a field mask of all fields has been specified).\nNote that in the presence\ + \ of schema evolution, this may mean that\nfields the client does not know and\ + \ has therefore not filled into\nthe request will be reset to their default.\ + \ If this is unwanted\nbehavior, a specific service may require a client to\ + \ always specify\na field mask, producing an error if not.\n\nAs with get operations,\ + \ the location of the resource which\ndescribes the updated values in the request\ + \ message depends on the\noperation kind. In any case, the effect of the field\ + \ mask is\nrequired to be honored by the API.\n\n## Considerations for HTTP\ + \ REST\n\nThe HTTP kind of an update operation which uses a field mask must\n\ + be set to PATCH instead of PUT in order to satisfy HTTP semantics\n(PUT must\ + \ only be used for full updates).\n\n# JSON Encoding of Field Masks\n\nIn JSON,\ + \ a field mask is encoded as a single string where paths are\nseparated by a\ + \ comma. Fields name in each path are converted\nto/from lower-camel naming\ + \ conventions.\n\nAs an example, consider the following message declarations:\n\ + \n message Profile {\n User user = 1;\n Photo photo = 2;\n }\n\ + \ message User {\n string display_name = 1;\n string address =\ + \ 2;\n }\n\nIn proto a field mask for `Profile` may look as such:\n\n \ + \ mask {\n paths: \"user.display_name\"\n paths: \"photo\"\n }\n\ + \nIn JSON, the same mask is represented as below:\n\n {\n mask: \"user.displayName,photo\"\ + \n }\n\n# Field Masks and Oneof Fields\n\nField masks treat fields in oneofs\ + \ just as regular fields. Consider the\nfollowing message:\n\n message SampleMessage\ + \ {\n oneof test_oneof {\n string name = 4;\n SubMessage\ + \ sub_message = 9;\n }\n }\n\nThe field mask can be:\n\n mask {\n\ + \ paths: \"name\"\n }\n\nOr:\n\n mask {\n paths: \"sub_message\"\ + \n }\n\nNote that oneof type names (\"test_oneof\" in this case) cannot be\ + \ used in\npaths.\n\n## Field Mask Verification\n\nThe implementation of any\ + \ API method which has a FieldMask type field in the\nrequest should verify\ + \ the included field paths, and return an\n`INVALID_ARGUMENT` error if any path\ + \ is duplicated or unmappable." + provenanceBuildProvenance: + type: "object" + properties: + id: + type: "string" + description: "Required. Unique identifier of the build." + project_id: + type: "string" + description: "ID of the project." + commands: + type: "array" + description: "Commands requested by the build." + items: + $ref: "#/definitions/provenanceCommand" + built_artifacts: + type: "array" + description: "Output of the build." + items: + $ref: "#/definitions/v1beta1provenanceArtifact" + create_time: + type: "string" + format: "date-time" + description: "Time at which the build was created." + start_time: + type: "string" + format: "date-time" + description: "Time at which execution of the build was started." + end_time: + type: "string" + format: "date-time" + description: "Time at which execution of the build was finished." + creator: + type: "string" + description: "E-mail address of the user who initiated this build. Note that\ + \ this was the\nuser's e-mail address at the time the build was initiated;\ + \ this address may\nnot represent the same end-user for all time." + logs_uri: + type: "string" + description: "URI where any logs for this provenance were written." + source_provenance: + description: "Details of the Source input to the build." + $ref: "#/definitions/provenanceSource" + trigger_id: + type: "string" + description: "Trigger identifier if the build was triggered automatically;\ + \ empty if not." + build_options: + type: "object" + description: "Special options applied to this build. This is a catch-all field\ + \ where\nbuild providers can enter any desired additional details." + additionalProperties: + type: "string" + builder_version: + type: "string" + description: "Version string of the builder at the time this build was executed." + description: "Provenance of a build. Contains all information needed to verify\ + \ the full\ndetails about the build from source to completion." + example: + creator: "creator" + create_time: "2000-01-23T04:56:07.000+00:00" + trigger_id: "trigger_id" + end_time: "2000-01-23T04:56:07.000+00:00" + built_artifacts: + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + logs_uri: "logs_uri" + builder_version: "builder_version" + source_provenance: + artifact_storage_source_uri: "artifact_storage_source_uri" + additional_contexts: + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + file_hashes: + key: + file_hash: + - type: {} + value: "value" + - type: {} + value: "value" + context: + git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + start_time: "2000-01-23T04:56:07.000+00:00" + project_id: "project_id" + id: "id" + commands: + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + build_options: + key: "build_options" + provenanceCommand: + type: "object" + properties: + name: + type: "string" + description: "Required. Name of the command, as presented on the command line,\ + \ or if the\ncommand is packaged as a Docker container, as presented to\ + \ `docker pull`." + env: + type: "array" + description: "Environment variables set before running this command." + items: + type: "string" + args: + type: "array" + description: "Command-line arguments used when executing this command." + items: + type: "string" + dir: + type: "string" + description: "Working directory (relative to project source root) used when\ + \ running this\ncommand." + id: + type: "string" + description: "Optional unique identifier for this command, used in wait_for\ + \ to reference\nthis command as a dependency." + wait_for: + type: "array" + description: "The ID(s) of the command(s) that this command depends on." + items: + type: "string" + description: "Command describes a step performed as part of the build pipeline." + example: + args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + provenanceFileHashes: + type: "object" + properties: + file_hash: + type: "array" + description: "Required. Collection of file hashes." + items: + $ref: "#/definitions/provenanceHash" + description: "Container message for hashes of byte content of files, used in source\n\ + messages to verify integrity of source input to the build." + example: + file_hash: + - type: {} + value: "value" + - type: {} + value: "value" + provenanceHash: + type: "object" + properties: + type: + description: "Required. The type of hash that was performed." + $ref: "#/definitions/HashHashType" + value: + type: "string" + format: "byte" + description: "Required. The hash value." + pattern: "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$" + description: "Container message for hash values." + example: + type: {} + value: "value" + provenanceSource: + type: "object" + properties: + artifact_storage_source_uri: + type: "string" + description: "If provided, the input binary artifacts for the build came from\ + \ this\nlocation." + file_hashes: + type: "object" + description: "Hash(es) of the build source, which can be used to verify that\ + \ the original\nsource integrity was maintained in the build.\n\nThe keys\ + \ to this map are file paths used as build source and the values\ncontain\ + \ the hash values for those files.\n\nIf the build source came in a single\ + \ package such as a gzipped tarfile\n(.tar.gz), the FileHash will be for\ + \ the single path to that file." + additionalProperties: + $ref: "#/definitions/provenanceFileHashes" + context: + description: "If provided, the source code used for the build came from this\ + \ location." + $ref: "#/definitions/sourceSourceContext" + additional_contexts: + type: "array" + description: "If provided, some of the source code used for the build may\ + \ be found in\nthese locations, in the case where the source repository\ + \ had multiple\nremotes or submodules. This list will not include the context\ + \ specified in\nthe context field." + items: + $ref: "#/definitions/sourceSourceContext" + description: "Source describes the location of the source used for the build." + example: + artifact_storage_source_uri: "artifact_storage_source_uri" + additional_contexts: + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + file_hashes: + key: + file_hash: + - type: {} + value: "value" + - type: {} + value: "value" + context: + git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + rpcStatus: + type: "object" + properties: + code: + type: "integer" + format: "int32" + description: "The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]." + message: + type: "string" + description: "A developer-facing error message, which should be in English.\ + \ Any\nuser-facing error message should be localized and sent in the\n[google.rpc.Status.details][google.rpc.Status.details]\ + \ field, or localized by the client." + details: + type: "array" + description: "A list of messages that carry the error details. There is a\ + \ common set of\nmessage types for APIs to use." + items: + $ref: "#/definitions/protobufAny" + title: "The `Status` type defines a logical error model that is suitable for different\n\ + programming environments, including REST APIs and RPC APIs. It is used by\n\ + [gRPC](https://github.com/grpc). The error model is designed to be:" + description: "- Simple to use and understand for most users\n- Flexible enough\ + \ to meet unexpected needs\n\n# Overview\n\nThe `Status` message contains three\ + \ pieces of data: error code, error message,\nand error details. The error code\ + \ should be an enum value of\n[google.rpc.Code][google.rpc.Code], but it may\ + \ accept additional error codes if needed. The\nerror message should be a developer-facing\ + \ English message that helps\ndevelopers *understand* and *resolve* the error.\ + \ If a localized user-facing\nerror message is needed, put the localized message\ + \ in the error details or\nlocalize it in the client. The optional error details\ + \ may contain arbitrary\ninformation about the error. There is a predefined\ + \ set of error detail types\nin the package `google.rpc` that can be used for\ + \ common error conditions.\n\n# Language mapping\n\nThe `Status` message is\ + \ the logical representation of the error model, but it\nis not necessarily\ + \ the actual wire format. When the `Status` message is\nexposed in different\ + \ client libraries and different wire protocols, it can be\nmapped differently.\ + \ For example, it will likely be mapped to some exceptions\nin Java, but more\ + \ likely mapped to some error codes in C.\n\n# Other uses\n\nThe error model\ + \ and the `Status` message can be used in a variety of\nenvironments, either\ + \ with or without APIs, to provide a\nconsistent developer experience across\ + \ different environments.\n\nExample uses of this error model include:\n\n-\ + \ Partial errors. If a service needs to return partial errors to the client,\n\ + \ it may embed the `Status` in the normal response to indicate the partial\n\ + \ errors.\n\n- Workflow errors. A typical workflow has multiple steps. Each\ + \ step may\n have a `Status` message for error reporting.\n\n- Batch operations.\ + \ If a client uses batch request and batch response, the\n `Status` message\ + \ should be used directly inside batch response, one for\n each error sub-response.\n\ + \n- Asynchronous operations. If an API call embeds asynchronous operation\n\ + \ results in its response, the status of those operations should be\n \ + \ represented directly using the `Status` message.\n\n- Logging. If some API\ + \ errors are stored in logs, the message `Status` could\n be used directly\ + \ after any stripping needed for security/privacy reasons." + example: + code: 1 + details: + - value: "value" + type_url: "type_url" + - value: "value" + type_url: "type_url" + message: "message" + sourceAliasContext: + type: "object" + properties: + kind: + description: "The alias kind." + $ref: "#/definitions/AliasContextKind" + name: + type: "string" + description: "The alias name." + description: "An alias to a repo revision." + example: + kind: {} + name: "name" + sourceCloudRepoSourceContext: + type: "object" + properties: + repo_id: + description: "The ID of the repo." + $ref: "#/definitions/sourceRepoId" + revision_id: + type: "string" + description: "A revision ID." + alias_context: + description: "An alias, which may be a branch or tag." + $ref: "#/definitions/sourceAliasContext" + description: "A CloudRepoSourceContext denotes a particular revision in a Google\ + \ Cloud\nSource Repo." + example: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + sourceGerritSourceContext: + type: "object" + properties: + host_uri: + type: "string" + description: "The URI of a running Gerrit instance." + gerrit_project: + type: "string" + description: "The full project name within the host. Projects may be nested,\ + \ so\n\"project/subproject\" is a valid project name. The \"repo name\"\ + \ is the\nhostURI/project." + revision_id: + type: "string" + description: "A revision (commit) ID." + alias_context: + description: "An alias, which may be a branch or tag." + $ref: "#/definitions/sourceAliasContext" + description: "A SourceContext referring to a Gerrit project." + example: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + sourceGitSourceContext: + type: "object" + properties: + url: + type: "string" + description: "Git repository URL." + revision_id: + type: "string" + description: "Git commit hash." + description: "A GitSourceContext denotes a particular revision in a third party\ + \ Git\nrepository (e.g., GitHub)." + example: + url: "url" + revision_id: "revision_id" + sourceProjectRepoId: + type: "object" + properties: + project_id: + type: "string" + description: "The ID of the project." + repo_name: + type: "string" + description: "The name of the repo. Leave empty for the default repo." + description: "Selects a repo using a Google Cloud Platform project ID (e.g.,\n\ + winged-cargo-31) and a repo name within that project." + example: + project_id: "project_id" + repo_name: "repo_name" + sourceRepoId: + type: "object" + properties: + project_repo_id: + description: "A combination of a project ID and a repo name." + $ref: "#/definitions/sourceProjectRepoId" + uid: + type: "string" + description: "A server-assigned, globally unique identifier." + description: "A unique identifier for a Cloud Repo." + example: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + sourceSourceContext: + type: "object" + properties: + cloud_repo: + description: "A SourceContext referring to a revision in a Google Cloud Source\ + \ Repo." + $ref: "#/definitions/sourceCloudRepoSourceContext" + gerrit: + description: "A SourceContext referring to a Gerrit project." + $ref: "#/definitions/sourceGerritSourceContext" + git: + description: "A SourceContext referring to any third party Git repo (e.g.,\ + \ GitHub)." + $ref: "#/definitions/sourceGitSourceContext" + labels: + type: "object" + description: "Labels with user defined metadata." + additionalProperties: + type: "string" + description: "A SourceContext is a reference to a tree of files. A SourceContext\ + \ together\nwith a path point to a unique revision of a single file or directory." + example: + git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + v1beta1BatchCreateNotesRequest: + type: "object" + properties: + parent: + type: "string" + description: "The name of the project in the form of `projects/[PROJECT_ID]`,\ + \ under which\nthe notes are to be created." + notes: + type: "object" + description: "The notes to create. Max allowed length is 1000." + additionalProperties: + $ref: "#/definitions/v1beta1Note" + description: "Request to create notes in batch." + v1beta1BatchCreateNotesResponse: + type: "object" + properties: + notes: + type: "array" + description: "The notes that were created." + items: + $ref: "#/definitions/v1beta1Note" + description: "Response for creating notes in batch." + example: + notes: + - attestation_authority: + hint: + human_readable_name: "human_readable_name" + short_description: "short_description" + related_note_names: + - "related_note_names" + - "related_note_names" + package: + name: "name" + distribution: + - latest_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + cpe_uri: "cpe_uri" + description: "description" + maintainer: "maintainer" + url: "url" + architecture: {} + - latest_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + cpe_uri: "cpe_uri" + description: "description" + maintainer: "maintainer" + url: "url" + architecture: {} + create_time: "2000-01-23T04:56:07.000+00:00" + kind: {} + related_url: + - label: "label" + url: "url" + - label: "label" + url: "url" + expiration_time: "2000-01-23T04:56:07.000+00:00" + intoto: + expected_products: + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + step_name: "step_name" + signing_keys: + - key_type: "key_type" + key_scheme: "key_scheme" + key_id: "key_id" + public_key_value: "public_key_value" + - key_type: "key_type" + key_scheme: "key_scheme" + key_id: "key_id" + public_key_value: "public_key_value" + expected_command: + - "expected_command" + - "expected_command" + threshold: "threshold" + expected_materials: + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + long_description: "long_description" + vulnerability: + severity: {} + cvss_score: 0.8008282 + cvss_v3: + attack_complexity: {} + base_score: 1.4658129 + user_interaction: {} + scope: {} + impact_score: 5.637377 + confidentiality_impact: {} + attack_vector: {} + exploitability_score: 5.962134 + privileges_required: {} + details: + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + package: "package" + cpe_uri: "cpe_uri" + max_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + description: "description" + min_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + source_update_time: "2000-01-23T04:56:07.000+00:00" + package_type: "package_type" + is_obsolete: true + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + package: "package" + cpe_uri: "cpe_uri" + max_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + description: "description" + min_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + source_update_time: "2000-01-23T04:56:07.000+00:00" + package_type: "package_type" + is_obsolete: true + source_update_time: "2000-01-23T04:56:07.000+00:00" + windows_details: + - cpe_uri: "cpe_uri" + fixing_kbs: + - name: "name" + url: "url" + - name: "name" + url: "url" + name: "name" + description: "description" + - cpe_uri: "cpe_uri" + fixing_kbs: + - name: "name" + url: "url" + - name: "name" + url: "url" + name: "name" + description: "description" + update_time: "2000-01-23T04:56:07.000+00:00" + base_image: + resource_url: "resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + deployable: + resource_uri: + - "resource_uri" + - "resource_uri" + build: + signature: + public_key: "public_key" + key_type: {} + signature: "signature" + key_id: "key_id" + builder_version: "builder_version" + discovery: {} + name: "name" + - attestation_authority: + hint: + human_readable_name: "human_readable_name" + short_description: "short_description" + related_note_names: + - "related_note_names" + - "related_note_names" + package: + name: "name" + distribution: + - latest_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + cpe_uri: "cpe_uri" + description: "description" + maintainer: "maintainer" + url: "url" + architecture: {} + - latest_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + cpe_uri: "cpe_uri" + description: "description" + maintainer: "maintainer" + url: "url" + architecture: {} + create_time: "2000-01-23T04:56:07.000+00:00" + kind: {} + related_url: + - label: "label" + url: "url" + - label: "label" + url: "url" + expiration_time: "2000-01-23T04:56:07.000+00:00" + intoto: + expected_products: + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + step_name: "step_name" + signing_keys: + - key_type: "key_type" + key_scheme: "key_scheme" + key_id: "key_id" + public_key_value: "public_key_value" + - key_type: "key_type" + key_scheme: "key_scheme" + key_id: "key_id" + public_key_value: "public_key_value" + expected_command: + - "expected_command" + - "expected_command" + threshold: "threshold" + expected_materials: + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + long_description: "long_description" + vulnerability: + severity: {} + cvss_score: 0.8008282 + cvss_v3: + attack_complexity: {} + base_score: 1.4658129 + user_interaction: {} + scope: {} + impact_score: 5.637377 + confidentiality_impact: {} + attack_vector: {} + exploitability_score: 5.962134 + privileges_required: {} + details: + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + package: "package" + cpe_uri: "cpe_uri" + max_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + description: "description" + min_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + source_update_time: "2000-01-23T04:56:07.000+00:00" + package_type: "package_type" + is_obsolete: true + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + package: "package" + cpe_uri: "cpe_uri" + max_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + description: "description" + min_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + source_update_time: "2000-01-23T04:56:07.000+00:00" + package_type: "package_type" + is_obsolete: true + source_update_time: "2000-01-23T04:56:07.000+00:00" + windows_details: + - cpe_uri: "cpe_uri" + fixing_kbs: + - name: "name" + url: "url" + - name: "name" + url: "url" + name: "name" + description: "description" + - cpe_uri: "cpe_uri" + fixing_kbs: + - name: "name" + url: "url" + - name: "name" + url: "url" + name: "name" + description: "description" + update_time: "2000-01-23T04:56:07.000+00:00" + base_image: + resource_url: "resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + deployable: + resource_uri: + - "resource_uri" + - "resource_uri" + build: + signature: + public_key: "public_key" + key_type: {} + signature: "signature" + key_id: "key_id" + builder_version: "builder_version" + discovery: {} + name: "name" + v1beta1BatchCreateOccurrencesRequest: + type: "object" + properties: + parent: + type: "string" + description: "The name of the project in the form of `projects/[PROJECT_ID]`,\ + \ under which\nthe occurrences are to be created." + occurrences: + type: "array" + description: "The occurrences to create. Max allowed length is 1000." + items: + $ref: "#/definitions/v1beta1Occurrence" + description: "Request to create occurrences in batch." + v1beta1BatchCreateOccurrencesResponse: + type: "object" + properties: + occurrences: + type: "array" + description: "The occurrences that were created." + items: + $ref: "#/definitions/v1beta1Occurrence" + description: "Response for creating occurrences in batch." + example: + occurrences: + - discovered: + discovered: + last_analysis_time: "2000-01-23T04:56:07.000+00:00" + analysis_status: {} + continuous_analysis: {} + analysis_status_error: + code: 1 + details: + - value: "value" + type_url: "type_url" + - value: "value" + type_url: "type_url" + message: "message" + attestation: + attestation: + pgp_signed_attestation: + content_type: {} + signature: "signature" + pgp_key_id: "pgp_key_id" + generic_signed_attestation: + content_type: {} + serialized_payload: "serialized_payload" + signatures: + - signature: "signature" + public_key_id: "public_key_id" + - signature: "signature" + public_key_id: "public_key_id" + create_time: "2000-01-23T04:56:07.000+00:00" + resource: + name: "name" + content_hash: + type: {} + value: "value" + uri: "uri" + kind: {} + intoto: + link: + environment: + custom_values: + key: "custom_values" + effective_command: + - "effective_command" + - "effective_command" + materials: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + byproducts: + custom_values: + key: "custom_values" + products: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + signatures: + - key_id: "key_id" + signature: "signature" + - key_id: "key_id" + signature: "signature" + vulnerability: + severity: {} + short_description: "short_description" + cvss_score: 0.8008282 + long_description: "long_description" + related_urls: + - label: "label" + url: "url" + - label: "label" + url: "url" + type: "type" + package_issue: + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + remediation: "remediation" + update_time: "2000-01-23T04:56:07.000+00:00" + build: + provenance: + creator: "creator" + create_time: "2000-01-23T04:56:07.000+00:00" + trigger_id: "trigger_id" + end_time: "2000-01-23T04:56:07.000+00:00" + built_artifacts: + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + logs_uri: "logs_uri" + builder_version: "builder_version" + source_provenance: + artifact_storage_source_uri: "artifact_storage_source_uri" + additional_contexts: + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + file_hashes: + key: + file_hash: + - type: {} + value: "value" + - type: {} + value: "value" + context: + git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + start_time: "2000-01-23T04:56:07.000+00:00" + project_id: "project_id" + id: "id" + commands: + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + build_options: + key: "build_options" + provenance_bytes: "provenance_bytes" + installation: + installation: + name: "name" + location: + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + name: "name" + derived_image: + derived_image: + distance: 6 + base_resource_url: "base_resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + layer_info: + - arguments: "arguments" + directive: {} + - arguments: "arguments" + directive: {} + note_name: "note_name" + deployment: + deployment: + user_email: "user_email" + address: "address" + resource_uri: + - "resource_uri" + - "resource_uri" + undeploy_time: "2000-01-23T04:56:07.000+00:00" + deploy_time: "2000-01-23T04:56:07.000+00:00" + config: "config" + platform: {} + - discovered: + discovered: + last_analysis_time: "2000-01-23T04:56:07.000+00:00" + analysis_status: {} + continuous_analysis: {} + analysis_status_error: + code: 1 + details: + - value: "value" + type_url: "type_url" + - value: "value" + type_url: "type_url" + message: "message" + attestation: + attestation: + pgp_signed_attestation: + content_type: {} + signature: "signature" + pgp_key_id: "pgp_key_id" + generic_signed_attestation: + content_type: {} + serialized_payload: "serialized_payload" + signatures: + - signature: "signature" + public_key_id: "public_key_id" + - signature: "signature" + public_key_id: "public_key_id" + create_time: "2000-01-23T04:56:07.000+00:00" + resource: + name: "name" + content_hash: + type: {} + value: "value" + uri: "uri" + kind: {} + intoto: + link: + environment: + custom_values: + key: "custom_values" + effective_command: + - "effective_command" + - "effective_command" + materials: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + byproducts: + custom_values: + key: "custom_values" + products: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + signatures: + - key_id: "key_id" + signature: "signature" + - key_id: "key_id" + signature: "signature" + vulnerability: + severity: {} + short_description: "short_description" + cvss_score: 0.8008282 + long_description: "long_description" + related_urls: + - label: "label" + url: "url" + - label: "label" + url: "url" + type: "type" + package_issue: + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + remediation: "remediation" + update_time: "2000-01-23T04:56:07.000+00:00" + build: + provenance: + creator: "creator" + create_time: "2000-01-23T04:56:07.000+00:00" + trigger_id: "trigger_id" + end_time: "2000-01-23T04:56:07.000+00:00" + built_artifacts: + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + logs_uri: "logs_uri" + builder_version: "builder_version" + source_provenance: + artifact_storage_source_uri: "artifact_storage_source_uri" + additional_contexts: + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + file_hashes: + key: + file_hash: + - type: {} + value: "value" + - type: {} + value: "value" + context: + git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + start_time: "2000-01-23T04:56:07.000+00:00" + project_id: "project_id" + id: "id" + commands: + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + build_options: + key: "build_options" + provenance_bytes: "provenance_bytes" + installation: + installation: + name: "name" + location: + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + name: "name" + derived_image: + derived_image: + distance: 6 + base_resource_url: "base_resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + layer_info: + - arguments: "arguments" + directive: {} + - arguments: "arguments" + directive: {} + note_name: "note_name" + deployment: + deployment: + user_email: "user_email" + address: "address" + resource_uri: + - "resource_uri" + - "resource_uri" + undeploy_time: "2000-01-23T04:56:07.000+00:00" + deploy_time: "2000-01-23T04:56:07.000+00:00" + config: "config" + platform: {} + v1beta1ListNoteOccurrencesResponse: + type: "object" + properties: + occurrences: + type: "array" + description: "The occurrences attached to the specified note." + items: + $ref: "#/definitions/v1beta1Occurrence" + next_page_token: + type: "string" + description: "Token to provide to skip to a particular spot in the list." + description: "Response for listing occurrences for a note." + example: + occurrences: + - discovered: + discovered: + last_analysis_time: "2000-01-23T04:56:07.000+00:00" + analysis_status: {} + continuous_analysis: {} + analysis_status_error: + code: 1 + details: + - value: "value" + type_url: "type_url" + - value: "value" + type_url: "type_url" + message: "message" + attestation: + attestation: + pgp_signed_attestation: + content_type: {} + signature: "signature" + pgp_key_id: "pgp_key_id" + generic_signed_attestation: + content_type: {} + serialized_payload: "serialized_payload" + signatures: + - signature: "signature" + public_key_id: "public_key_id" + - signature: "signature" + public_key_id: "public_key_id" + create_time: "2000-01-23T04:56:07.000+00:00" + resource: + name: "name" + content_hash: + type: {} + value: "value" + uri: "uri" + kind: {} + intoto: + link: + environment: + custom_values: + key: "custom_values" + effective_command: + - "effective_command" + - "effective_command" + materials: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + byproducts: + custom_values: + key: "custom_values" + products: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + signatures: + - key_id: "key_id" + signature: "signature" + - key_id: "key_id" + signature: "signature" + vulnerability: + severity: {} + short_description: "short_description" + cvss_score: 0.8008282 + long_description: "long_description" + related_urls: + - label: "label" + url: "url" + - label: "label" + url: "url" + type: "type" + package_issue: + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + remediation: "remediation" + update_time: "2000-01-23T04:56:07.000+00:00" + build: + provenance: + creator: "creator" + create_time: "2000-01-23T04:56:07.000+00:00" + trigger_id: "trigger_id" + end_time: "2000-01-23T04:56:07.000+00:00" + built_artifacts: + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + logs_uri: "logs_uri" + builder_version: "builder_version" + source_provenance: + artifact_storage_source_uri: "artifact_storage_source_uri" + additional_contexts: + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + file_hashes: + key: + file_hash: + - type: {} + value: "value" + - type: {} + value: "value" + context: + git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + start_time: "2000-01-23T04:56:07.000+00:00" + project_id: "project_id" + id: "id" + commands: + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + build_options: + key: "build_options" + provenance_bytes: "provenance_bytes" + installation: + installation: + name: "name" + location: + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + name: "name" + derived_image: + derived_image: + distance: 6 + base_resource_url: "base_resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + layer_info: + - arguments: "arguments" + directive: {} + - arguments: "arguments" + directive: {} + note_name: "note_name" + deployment: + deployment: + user_email: "user_email" + address: "address" + resource_uri: + - "resource_uri" + - "resource_uri" + undeploy_time: "2000-01-23T04:56:07.000+00:00" + deploy_time: "2000-01-23T04:56:07.000+00:00" + config: "config" + platform: {} + - discovered: + discovered: + last_analysis_time: "2000-01-23T04:56:07.000+00:00" + analysis_status: {} + continuous_analysis: {} + analysis_status_error: + code: 1 + details: + - value: "value" + type_url: "type_url" + - value: "value" + type_url: "type_url" + message: "message" + attestation: + attestation: + pgp_signed_attestation: + content_type: {} + signature: "signature" + pgp_key_id: "pgp_key_id" + generic_signed_attestation: + content_type: {} + serialized_payload: "serialized_payload" + signatures: + - signature: "signature" + public_key_id: "public_key_id" + - signature: "signature" + public_key_id: "public_key_id" + create_time: "2000-01-23T04:56:07.000+00:00" + resource: + name: "name" + content_hash: + type: {} + value: "value" + uri: "uri" + kind: {} + intoto: + link: + environment: + custom_values: + key: "custom_values" + effective_command: + - "effective_command" + - "effective_command" + materials: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + byproducts: + custom_values: + key: "custom_values" + products: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + signatures: + - key_id: "key_id" + signature: "signature" + - key_id: "key_id" + signature: "signature" + vulnerability: + severity: {} + short_description: "short_description" + cvss_score: 0.8008282 + long_description: "long_description" + related_urls: + - label: "label" + url: "url" + - label: "label" + url: "url" + type: "type" + package_issue: + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + remediation: "remediation" + update_time: "2000-01-23T04:56:07.000+00:00" + build: + provenance: + creator: "creator" + create_time: "2000-01-23T04:56:07.000+00:00" + trigger_id: "trigger_id" + end_time: "2000-01-23T04:56:07.000+00:00" + built_artifacts: + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + logs_uri: "logs_uri" + builder_version: "builder_version" + source_provenance: + artifact_storage_source_uri: "artifact_storage_source_uri" + additional_contexts: + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + file_hashes: + key: + file_hash: + - type: {} + value: "value" + - type: {} + value: "value" + context: + git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + start_time: "2000-01-23T04:56:07.000+00:00" + project_id: "project_id" + id: "id" + commands: + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + build_options: + key: "build_options" + provenance_bytes: "provenance_bytes" + installation: + installation: + name: "name" + location: + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + name: "name" + derived_image: + derived_image: + distance: 6 + base_resource_url: "base_resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + layer_info: + - arguments: "arguments" + directive: {} + - arguments: "arguments" + directive: {} + note_name: "note_name" + deployment: + deployment: + user_email: "user_email" + address: "address" + resource_uri: + - "resource_uri" + - "resource_uri" + undeploy_time: "2000-01-23T04:56:07.000+00:00" + deploy_time: "2000-01-23T04:56:07.000+00:00" + config: "config" + platform: {} + next_page_token: "next_page_token" + v1beta1ListNotesResponse: + type: "object" + properties: + notes: + type: "array" + description: "The notes requested." + items: + $ref: "#/definitions/v1beta1Note" + next_page_token: + type: "string" + description: "The next pagination token in the list response. It should be\ + \ used as\n`page_token` for the following request. An empty value means\ + \ no more\nresults." + description: "Response for listing notes." + example: + notes: + - attestation_authority: + hint: + human_readable_name: "human_readable_name" + short_description: "short_description" + related_note_names: + - "related_note_names" + - "related_note_names" + package: + name: "name" + distribution: + - latest_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + cpe_uri: "cpe_uri" + description: "description" + maintainer: "maintainer" + url: "url" + architecture: {} + - latest_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + cpe_uri: "cpe_uri" + description: "description" + maintainer: "maintainer" + url: "url" + architecture: {} + create_time: "2000-01-23T04:56:07.000+00:00" + kind: {} + related_url: + - label: "label" + url: "url" + - label: "label" + url: "url" + expiration_time: "2000-01-23T04:56:07.000+00:00" + intoto: + expected_products: + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + step_name: "step_name" + signing_keys: + - key_type: "key_type" + key_scheme: "key_scheme" + key_id: "key_id" + public_key_value: "public_key_value" + - key_type: "key_type" + key_scheme: "key_scheme" + key_id: "key_id" + public_key_value: "public_key_value" + expected_command: + - "expected_command" + - "expected_command" + threshold: "threshold" + expected_materials: + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + long_description: "long_description" + vulnerability: + severity: {} + cvss_score: 0.8008282 + cvss_v3: + attack_complexity: {} + base_score: 1.4658129 + user_interaction: {} + scope: {} + impact_score: 5.637377 + confidentiality_impact: {} + attack_vector: {} + exploitability_score: 5.962134 + privileges_required: {} + details: + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + package: "package" + cpe_uri: "cpe_uri" + max_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + description: "description" + min_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + source_update_time: "2000-01-23T04:56:07.000+00:00" + package_type: "package_type" + is_obsolete: true + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + package: "package" + cpe_uri: "cpe_uri" + max_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + description: "description" + min_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + source_update_time: "2000-01-23T04:56:07.000+00:00" + package_type: "package_type" + is_obsolete: true + source_update_time: "2000-01-23T04:56:07.000+00:00" + windows_details: + - cpe_uri: "cpe_uri" + fixing_kbs: + - name: "name" + url: "url" + - name: "name" + url: "url" + name: "name" + description: "description" + - cpe_uri: "cpe_uri" + fixing_kbs: + - name: "name" + url: "url" + - name: "name" + url: "url" + name: "name" + description: "description" + update_time: "2000-01-23T04:56:07.000+00:00" + base_image: + resource_url: "resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + deployable: + resource_uri: + - "resource_uri" + - "resource_uri" + build: + signature: + public_key: "public_key" + key_type: {} + signature: "signature" + key_id: "key_id" + builder_version: "builder_version" + discovery: {} + name: "name" + - attestation_authority: + hint: + human_readable_name: "human_readable_name" + short_description: "short_description" + related_note_names: + - "related_note_names" + - "related_note_names" + package: + name: "name" + distribution: + - latest_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + cpe_uri: "cpe_uri" + description: "description" + maintainer: "maintainer" + url: "url" + architecture: {} + - latest_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + cpe_uri: "cpe_uri" + description: "description" + maintainer: "maintainer" + url: "url" + architecture: {} + create_time: "2000-01-23T04:56:07.000+00:00" + kind: {} + related_url: + - label: "label" + url: "url" + - label: "label" + url: "url" + expiration_time: "2000-01-23T04:56:07.000+00:00" + intoto: + expected_products: + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + step_name: "step_name" + signing_keys: + - key_type: "key_type" + key_scheme: "key_scheme" + key_id: "key_id" + public_key_value: "public_key_value" + - key_type: "key_type" + key_scheme: "key_scheme" + key_id: "key_id" + public_key_value: "public_key_value" + expected_command: + - "expected_command" + - "expected_command" + threshold: "threshold" + expected_materials: + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + long_description: "long_description" + vulnerability: + severity: {} + cvss_score: 0.8008282 + cvss_v3: + attack_complexity: {} + base_score: 1.4658129 + user_interaction: {} + scope: {} + impact_score: 5.637377 + confidentiality_impact: {} + attack_vector: {} + exploitability_score: 5.962134 + privileges_required: {} + details: + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + package: "package" + cpe_uri: "cpe_uri" + max_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + description: "description" + min_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + source_update_time: "2000-01-23T04:56:07.000+00:00" + package_type: "package_type" + is_obsolete: true + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + package: "package" + cpe_uri: "cpe_uri" + max_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + description: "description" + min_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + source_update_time: "2000-01-23T04:56:07.000+00:00" + package_type: "package_type" + is_obsolete: true + source_update_time: "2000-01-23T04:56:07.000+00:00" + windows_details: + - cpe_uri: "cpe_uri" + fixing_kbs: + - name: "name" + url: "url" + - name: "name" + url: "url" + name: "name" + description: "description" + - cpe_uri: "cpe_uri" + fixing_kbs: + - name: "name" + url: "url" + - name: "name" + url: "url" + name: "name" + description: "description" + update_time: "2000-01-23T04:56:07.000+00:00" + base_image: + resource_url: "resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + deployable: + resource_uri: + - "resource_uri" + - "resource_uri" + build: + signature: + public_key: "public_key" + key_type: {} + signature: "signature" + key_id: "key_id" + builder_version: "builder_version" + discovery: {} + name: "name" + next_page_token: "next_page_token" + v1beta1ListOccurrencesResponse: + type: "object" + properties: + occurrences: + type: "array" + description: "The occurrences requested." + items: + $ref: "#/definitions/v1beta1Occurrence" + next_page_token: + type: "string" + description: "The next pagination token in the list response. It should be\ + \ used as\n`page_token` for the following request. An empty value means\ + \ no more\nresults." + description: "Response for listing occurrences." + example: + occurrences: + - discovered: + discovered: + last_analysis_time: "2000-01-23T04:56:07.000+00:00" + analysis_status: {} + continuous_analysis: {} + analysis_status_error: + code: 1 + details: + - value: "value" + type_url: "type_url" + - value: "value" + type_url: "type_url" + message: "message" + attestation: + attestation: + pgp_signed_attestation: + content_type: {} + signature: "signature" + pgp_key_id: "pgp_key_id" + generic_signed_attestation: + content_type: {} + serialized_payload: "serialized_payload" + signatures: + - signature: "signature" + public_key_id: "public_key_id" + - signature: "signature" + public_key_id: "public_key_id" + create_time: "2000-01-23T04:56:07.000+00:00" + resource: + name: "name" + content_hash: + type: {} + value: "value" + uri: "uri" + kind: {} + intoto: + link: + environment: + custom_values: + key: "custom_values" + effective_command: + - "effective_command" + - "effective_command" + materials: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + byproducts: + custom_values: + key: "custom_values" + products: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + signatures: + - key_id: "key_id" + signature: "signature" + - key_id: "key_id" + signature: "signature" + vulnerability: + severity: {} + short_description: "short_description" + cvss_score: 0.8008282 + long_description: "long_description" + related_urls: + - label: "label" + url: "url" + - label: "label" + url: "url" + type: "type" + package_issue: + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + remediation: "remediation" + update_time: "2000-01-23T04:56:07.000+00:00" + build: + provenance: + creator: "creator" + create_time: "2000-01-23T04:56:07.000+00:00" + trigger_id: "trigger_id" + end_time: "2000-01-23T04:56:07.000+00:00" + built_artifacts: + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + logs_uri: "logs_uri" + builder_version: "builder_version" + source_provenance: + artifact_storage_source_uri: "artifact_storage_source_uri" + additional_contexts: + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + file_hashes: + key: + file_hash: + - type: {} + value: "value" + - type: {} + value: "value" + context: + git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + start_time: "2000-01-23T04:56:07.000+00:00" + project_id: "project_id" + id: "id" + commands: + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + build_options: + key: "build_options" + provenance_bytes: "provenance_bytes" + installation: + installation: + name: "name" + location: + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + name: "name" + derived_image: + derived_image: + distance: 6 + base_resource_url: "base_resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + layer_info: + - arguments: "arguments" + directive: {} + - arguments: "arguments" + directive: {} + note_name: "note_name" + deployment: + deployment: + user_email: "user_email" + address: "address" + resource_uri: + - "resource_uri" + - "resource_uri" + undeploy_time: "2000-01-23T04:56:07.000+00:00" + deploy_time: "2000-01-23T04:56:07.000+00:00" + config: "config" + platform: {} + - discovered: + discovered: + last_analysis_time: "2000-01-23T04:56:07.000+00:00" + analysis_status: {} + continuous_analysis: {} + analysis_status_error: + code: 1 + details: + - value: "value" + type_url: "type_url" + - value: "value" + type_url: "type_url" + message: "message" + attestation: + attestation: + pgp_signed_attestation: + content_type: {} + signature: "signature" + pgp_key_id: "pgp_key_id" + generic_signed_attestation: + content_type: {} + serialized_payload: "serialized_payload" + signatures: + - signature: "signature" + public_key_id: "public_key_id" + - signature: "signature" + public_key_id: "public_key_id" + create_time: "2000-01-23T04:56:07.000+00:00" + resource: + name: "name" + content_hash: + type: {} + value: "value" + uri: "uri" + kind: {} + intoto: + link: + environment: + custom_values: + key: "custom_values" + effective_command: + - "effective_command" + - "effective_command" + materials: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + byproducts: + custom_values: + key: "custom_values" + products: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + signatures: + - key_id: "key_id" + signature: "signature" + - key_id: "key_id" + signature: "signature" + vulnerability: + severity: {} + short_description: "short_description" + cvss_score: 0.8008282 + long_description: "long_description" + related_urls: + - label: "label" + url: "url" + - label: "label" + url: "url" + type: "type" + package_issue: + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + remediation: "remediation" + update_time: "2000-01-23T04:56:07.000+00:00" + build: + provenance: + creator: "creator" + create_time: "2000-01-23T04:56:07.000+00:00" + trigger_id: "trigger_id" + end_time: "2000-01-23T04:56:07.000+00:00" + built_artifacts: + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + logs_uri: "logs_uri" + builder_version: "builder_version" + source_provenance: + artifact_storage_source_uri: "artifact_storage_source_uri" + additional_contexts: + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + file_hashes: + key: + file_hash: + - type: {} + value: "value" + - type: {} + value: "value" + context: + git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + start_time: "2000-01-23T04:56:07.000+00:00" + project_id: "project_id" + id: "id" + commands: + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + build_options: + key: "build_options" + provenance_bytes: "provenance_bytes" + installation: + installation: + name: "name" + location: + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + name: "name" + derived_image: + derived_image: + distance: 6 + base_resource_url: "base_resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + layer_info: + - arguments: "arguments" + directive: {} + - arguments: "arguments" + directive: {} + note_name: "note_name" + deployment: + deployment: + user_email: "user_email" + address: "address" + resource_uri: + - "resource_uri" + - "resource_uri" + undeploy_time: "2000-01-23T04:56:07.000+00:00" + deploy_time: "2000-01-23T04:56:07.000+00:00" + config: "config" + platform: {} + next_page_token: "next_page_token" + v1beta1Note: + type: "object" + properties: + name: + type: "string" + description: "Output only. The name of the note in the form of\n`projects/[PROVIDER_ID]/notes/[NOTE_ID]`." + readOnly: true + short_description: + type: "string" + description: "A one sentence description of this note." + long_description: + type: "string" + description: "A detailed description of this note." + kind: + description: "Output only. The type of analysis. This field can be used as\ + \ a filter in\nlist requests." + readOnly: true + $ref: "#/definitions/v1beta1NoteKind" + related_url: + type: "array" + description: "URLs associated with this note." + items: + $ref: "#/definitions/v1beta1RelatedUrl" + expiration_time: + type: "string" + format: "date-time" + description: "Time of expiration for this note. Empty if note does not expire." + create_time: + type: "string" + format: "date-time" + description: "Output only. The time this note was created. This field can\ + \ be used as a\nfilter in list requests." + readOnly: true + update_time: + type: "string" + format: "date-time" + description: "Output only. The time this note was last updated. This field\ + \ can be used as\na filter in list requests." + readOnly: true + related_note_names: + type: "array" + description: "Other notes related to this note." + items: + type: "string" + vulnerability: + description: "A note describing a package vulnerability." + $ref: "#/definitions/vulnerabilityVulnerability" + build: + description: "A note describing build provenance for a verifiable build." + $ref: "#/definitions/buildBuild" + base_image: + description: "A note describing a base image." + $ref: "#/definitions/imageBasis" + package: + description: "A note describing a package hosted by various package managers." + $ref: "#/definitions/packagePackage" + deployable: + description: "A note describing something that can be deployed." + $ref: "#/definitions/deploymentDeployable" + discovery: + description: "A note describing the initial analysis of a resource." + $ref: "#/definitions/discoveryDiscovery" + attestation_authority: + description: "A note describing an attestation role." + $ref: "#/definitions/attestationAuthority" + intoto: + description: "A note describing an in-toto link." + $ref: "#/definitions/intotoInToto" + description: "A type of analysis that can be done for a resource." + example: + attestation_authority: + hint: + human_readable_name: "human_readable_name" + short_description: "short_description" + related_note_names: + - "related_note_names" + - "related_note_names" + package: + name: "name" + distribution: + - latest_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + cpe_uri: "cpe_uri" + description: "description" + maintainer: "maintainer" + url: "url" + architecture: {} + - latest_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + cpe_uri: "cpe_uri" + description: "description" + maintainer: "maintainer" + url: "url" + architecture: {} + create_time: "2000-01-23T04:56:07.000+00:00" + kind: {} + related_url: + - label: "label" + url: "url" + - label: "label" + url: "url" + expiration_time: "2000-01-23T04:56:07.000+00:00" + intoto: + expected_products: + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + step_name: "step_name" + signing_keys: + - key_type: "key_type" + key_scheme: "key_scheme" + key_id: "key_id" + public_key_value: "public_key_value" + - key_type: "key_type" + key_scheme: "key_scheme" + key_id: "key_id" + public_key_value: "public_key_value" + expected_command: + - "expected_command" + - "expected_command" + threshold: "threshold" + expected_materials: + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + - artifact_rule: + - "artifact_rule" + - "artifact_rule" + long_description: "long_description" + vulnerability: + severity: {} + cvss_score: 0.8008282 + cvss_v3: + attack_complexity: {} + base_score: 1.4658129 + user_interaction: {} + scope: {} + impact_score: 5.637377 + confidentiality_impact: {} + attack_vector: {} + exploitability_score: 5.962134 + privileges_required: {} + details: + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + package: "package" + cpe_uri: "cpe_uri" + max_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + description: "description" + min_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + source_update_time: "2000-01-23T04:56:07.000+00:00" + package_type: "package_type" + is_obsolete: true + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + package: "package" + cpe_uri: "cpe_uri" + max_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + description: "description" + min_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + source_update_time: "2000-01-23T04:56:07.000+00:00" + package_type: "package_type" + is_obsolete: true + source_update_time: "2000-01-23T04:56:07.000+00:00" + windows_details: + - cpe_uri: "cpe_uri" + fixing_kbs: + - name: "name" + url: "url" + - name: "name" + url: "url" + name: "name" + description: "description" + - cpe_uri: "cpe_uri" + fixing_kbs: + - name: "name" + url: "url" + - name: "name" + url: "url" + name: "name" + description: "description" + update_time: "2000-01-23T04:56:07.000+00:00" + base_image: + resource_url: "resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + deployable: + resource_uri: + - "resource_uri" + - "resource_uri" + build: + signature: + public_key: "public_key" + key_type: {} + signature: "signature" + key_id: "key_id" + builder_version: "builder_version" + discovery: {} + name: "name" + v1beta1NoteKind: + type: "string" + description: "Kind represents the kinds of notes supported.\n\n - NOTE_KIND_UNSPECIFIED:\ + \ Unknown.\n - VULNERABILITY: The note and occurrence represent a package vulnerability.\n\ + \ - BUILD: The note and occurrence assert build provenance.\n - IMAGE: This\ + \ represents an image basis relationship.\n - PACKAGE: This represents a package\ + \ installed via a package manager.\n - DEPLOYMENT: The note and occurrence track\ + \ deployment events.\n - DISCOVERY: The note and occurrence track the initial\ + \ discovery status of a resource.\n - ATTESTATION: This represents a logical\ + \ \"role\" that can attest to artifacts.\n - INTOTO: This represents an in-toto\ + \ link." + enum: + - "NOTE_KIND_UNSPECIFIED" + - "VULNERABILITY" + - "BUILD" + - "IMAGE" + - "PACKAGE" + - "DEPLOYMENT" + - "DISCOVERY" + - "ATTESTATION" + - "INTOTO" + default: "NOTE_KIND_UNSPECIFIED" + v1beta1Occurrence: + type: "object" + properties: + name: + type: "string" + description: "Output only. The name of the occurrence in the form of\n`projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`." + readOnly: true + resource: + description: "Required. Immutable. The resource for which the occurrence applies." + $ref: "#/definitions/v1beta1Resource" + note_name: + type: "string" + description: "Required. Immutable. The analysis note associated with this\ + \ occurrence, in\nthe form of `projects/[PROVIDER_ID]/notes/[NOTE_ID]`.\ + \ This field can be\nused as a filter in list requests." + kind: + description: "Output only. This explicitly denotes which of the occurrence\ + \ details are\nspecified. This field can be used as a filter in list requests." + readOnly: true + $ref: "#/definitions/v1beta1NoteKind" + remediation: + type: "string" + description: "A description of actions that can be taken to remedy the note." + create_time: + type: "string" + format: "date-time" + description: "Output only. The time this occurrence was created." + readOnly: true + update_time: + type: "string" + format: "date-time" + description: "Output only. The time this occurrence was last updated." + readOnly: true + vulnerability: + description: "Describes a security vulnerability." + $ref: "#/definitions/v1beta1vulnerabilityDetails" + build: + description: "Describes a verifiable build." + $ref: "#/definitions/v1beta1buildDetails" + derived_image: + description: "Describes how this resource derives from the basis in the associated\n\ + note." + $ref: "#/definitions/v1beta1imageDetails" + installation: + description: "Describes the installation of a package on the linked resource." + $ref: "#/definitions/v1beta1packageDetails" + deployment: + description: "Describes the deployment of an artifact on a runtime." + $ref: "#/definitions/v1beta1deploymentDetails" + discovered: + description: "Describes when a resource was discovered." + $ref: "#/definitions/v1beta1discoveryDetails" + attestation: + description: "Describes an attestation of an artifact." + $ref: "#/definitions/v1beta1attestationDetails" + intoto: + description: "Describes a specific in-toto link." + $ref: "#/definitions/v1beta1intotoDetails" + description: "An instance of an analysis type that has been found on a resource." + example: + discovered: + discovered: + last_analysis_time: "2000-01-23T04:56:07.000+00:00" + analysis_status: {} + continuous_analysis: {} + analysis_status_error: + code: 1 + details: + - value: "value" + type_url: "type_url" + - value: "value" + type_url: "type_url" + message: "message" + attestation: + attestation: + pgp_signed_attestation: + content_type: {} + signature: "signature" + pgp_key_id: "pgp_key_id" + generic_signed_attestation: + content_type: {} + serialized_payload: "serialized_payload" + signatures: + - signature: "signature" + public_key_id: "public_key_id" + - signature: "signature" + public_key_id: "public_key_id" + create_time: "2000-01-23T04:56:07.000+00:00" + resource: + name: "name" + content_hash: + type: {} + value: "value" + uri: "uri" + kind: {} + intoto: + link: + environment: + custom_values: + key: "custom_values" + effective_command: + - "effective_command" + - "effective_command" + materials: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + byproducts: + custom_values: + key: "custom_values" + products: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + signatures: + - key_id: "key_id" + signature: "signature" + - key_id: "key_id" + signature: "signature" + vulnerability: + severity: {} + short_description: "short_description" + cvss_score: 0.8008282 + long_description: "long_description" + related_urls: + - label: "label" + url: "url" + - label: "label" + url: "url" + type: "type" + package_issue: + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + remediation: "remediation" + update_time: "2000-01-23T04:56:07.000+00:00" + build: + provenance: + creator: "creator" + create_time: "2000-01-23T04:56:07.000+00:00" + trigger_id: "trigger_id" + end_time: "2000-01-23T04:56:07.000+00:00" + built_artifacts: + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + logs_uri: "logs_uri" + builder_version: "builder_version" + source_provenance: + artifact_storage_source_uri: "artifact_storage_source_uri" + additional_contexts: + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + file_hashes: + key: + file_hash: + - type: {} + value: "value" + - type: {} + value: "value" + context: + git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + start_time: "2000-01-23T04:56:07.000+00:00" + project_id: "project_id" + id: "id" + commands: + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + build_options: + key: "build_options" + provenance_bytes: "provenance_bytes" + installation: + installation: + name: "name" + location: + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + name: "name" + derived_image: + derived_image: + distance: 6 + base_resource_url: "base_resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + layer_info: + - arguments: "arguments" + directive: {} + - arguments: "arguments" + directive: {} + note_name: "note_name" + deployment: + deployment: + user_email: "user_email" + address: "address" + resource_uri: + - "resource_uri" + - "resource_uri" + undeploy_time: "2000-01-23T04:56:07.000+00:00" + deploy_time: "2000-01-23T04:56:07.000+00:00" + config: "config" + platform: {} + v1beta1RelatedUrl: + type: "object" + properties: + url: + type: "string" + description: "Specific URL associated with the resource." + label: + type: "string" + description: "Label to describe usage of the URL." + description: "Metadata for any related URL information." + example: + label: "label" + url: "url" + v1beta1Resource: + type: "object" + properties: + name: + type: "string" + description: "Deprecated, do not use. Use uri instead.\n\nThe name of the\ + \ resource. For example, the name of a Docker image -\n\"Debian\"." + uri: + type: "string" + description: "Required. The unique URI of the resource. For example,\n`https://gcr.io/project/image@sha256:foo`\ + \ for a Docker image." + content_hash: + description: "Deprecated, do not use. Use uri instead.\n\nThe hash of the\ + \ resource content. For example, the Docker digest." + $ref: "#/definitions/provenanceHash" + description: "An entity that can have metadata. For example, a Docker image." + example: + name: "name" + content_hash: + type: {} + value: "value" + uri: "uri" + v1beta1VulnerabilityOccurrencesSummary: + type: "object" + properties: + counts: + type: "array" + description: "A listing by resource of the number of fixable and total vulnerabilities." + items: + $ref: "#/definitions/VulnerabilityOccurrencesSummaryFixableTotalByDigest" + description: "A summary of how many vulnerability occurrences there are per resource\ + \ and\nseverity type." + example: + counts: + - severity: {} + resource: + name: "name" + content_hash: + type: {} + value: "value" + uri: "uri" + total_count: "total_count" + fixable_count: "fixable_count" + - severity: {} + resource: + name: "name" + content_hash: + type: {} + value: "value" + uri: "uri" + total_count: "total_count" + fixable_count: "fixable_count" + v1beta1attestationDetails: + type: "object" + properties: + attestation: + description: "Required. Attestation for the resource." + $ref: "#/definitions/attestationAttestation" + description: "Details of an attestation occurrence." + example: + attestation: + pgp_signed_attestation: + content_type: {} + signature: "signature" + pgp_key_id: "pgp_key_id" + generic_signed_attestation: + content_type: {} + serialized_payload: "serialized_payload" + signatures: + - signature: "signature" + public_key_id: "public_key_id" + - signature: "signature" + public_key_id: "public_key_id" + v1beta1buildDetails: + type: "object" + properties: + provenance: + description: "Required. The actual provenance for the build." + $ref: "#/definitions/provenanceBuildProvenance" + provenance_bytes: + type: "string" + description: "Serialized JSON representation of the provenance, used in generating\ + \ the\nbuild signature in the corresponding build note. After verifying\ + \ the\nsignature, `provenance_bytes` can be unmarshalled and compared to\ + \ the\nprovenance to confirm that it is unchanged. A base64-encoded string\n\ + representation of the provenance bytes is used for the signature in order\n\ + to interoperate with openssl which expects this format for signature\nverification.\n\ + \nThe serialized form is captured both to avoid ambiguity in how the\nprovenance\ + \ is marshalled to json as well to prevent incompatibilities with\nfuture\ + \ changes." + description: "Details of a build occurrence." + example: + provenance: + creator: "creator" + create_time: "2000-01-23T04:56:07.000+00:00" + trigger_id: "trigger_id" + end_time: "2000-01-23T04:56:07.000+00:00" + built_artifacts: + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + - names: + - "names" + - "names" + checksum: "checksum" + id: "id" + logs_uri: "logs_uri" + builder_version: "builder_version" + source_provenance: + artifact_storage_source_uri: "artifact_storage_source_uri" + additional_contexts: + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + - git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + file_hashes: + key: + file_hash: + - type: {} + value: "value" + - type: {} + value: "value" + context: + git: + url: "url" + revision_id: "revision_id" + cloud_repo: + repo_id: + uid: "uid" + project_repo_id: + project_id: "project_id" + repo_name: "repo_name" + alias_context: + kind: {} + name: "name" + revision_id: "revision_id" + gerrit: + gerrit_project: "gerrit_project" + alias_context: + kind: {} + name: "name" + host_uri: "host_uri" + revision_id: "revision_id" + labels: + key: "labels" + start_time: "2000-01-23T04:56:07.000+00:00" + project_id: "project_id" + id: "id" + commands: + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + - args: + - "args" + - "args" + name: "name" + id: "id" + wait_for: + - "wait_for" + - "wait_for" + env: + - "env" + - "env" + dir: "dir" + build_options: + key: "build_options" + provenance_bytes: "provenance_bytes" + v1beta1deploymentDetails: + type: "object" + properties: + deployment: + description: "Required. Deployment history for the resource." + $ref: "#/definitions/deploymentDeployment" + description: "Details of a deployment occurrence." + example: + deployment: + user_email: "user_email" + address: "address" + resource_uri: + - "resource_uri" + - "resource_uri" + undeploy_time: "2000-01-23T04:56:07.000+00:00" + deploy_time: "2000-01-23T04:56:07.000+00:00" + config: "config" + platform: {} + v1beta1discoveryDetails: + type: "object" + properties: + discovered: + description: "Required. Analysis status for the discovered resource." + $ref: "#/definitions/discoveryDiscovered" + description: "Details of a discovery occurrence." + example: + discovered: + last_analysis_time: "2000-01-23T04:56:07.000+00:00" + analysis_status: {} + continuous_analysis: {} + analysis_status_error: + code: 1 + details: + - value: "value" + type_url: "type_url" + - value: "value" + type_url: "type_url" + message: "message" + v1beta1imageDetails: + type: "object" + properties: + derived_image: + description: "Required. Immutable. The child image derived from the base image." + $ref: "#/definitions/imageDerived" + description: "Details of an image occurrence." + example: + derived_image: + distance: 6 + base_resource_url: "base_resource_url" + fingerprint: + v2_name: "v2_name" + v1_name: "v1_name" + v2_blob: + - "v2_blob" + - "v2_blob" + layer_info: + - arguments: "arguments" + directive: {} + - arguments: "arguments" + directive: {} + v1beta1intotoDetails: + type: "object" + properties: + signatures: + type: "array" + items: + $ref: "#/definitions/v1beta1intotoSignature" + link: + $ref: "#/definitions/intotoLink" + description: "This corresponds to a signed in-toto link - it is made up of one\ + \ or more\nsignatures and the in-toto link itself. This is used for occurrences\ + \ of a\nGrafeas in-toto note." + example: + link: + environment: + custom_values: + key: "custom_values" + effective_command: + - "effective_command" + - "effective_command" + materials: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + byproducts: + custom_values: + key: "custom_values" + products: + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + - resource_uri: "resource_uri" + hashes: + sha256: "sha256" + signatures: + - key_id: "key_id" + signature: "signature" + - key_id: "key_id" + signature: "signature" + v1beta1intotoSignature: + type: "object" + properties: + key_id: + type: "string" + signature: + type: "string" + description: "A signature object consists of the KeyID used and the signature\ + \ itself." + example: + key_id: "key_id" + signature: "signature" + v1beta1packageDetails: + type: "object" + properties: + installation: + description: "Required. Where the package was installed." + $ref: "#/definitions/packageInstallation" + description: "Details of a package occurrence." + example: + installation: + name: "name" + location: + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + - path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + v1beta1packageLocation: + type: "object" + properties: + cpe_uri: + type: "string" + description: "Required. The CPE URI in [CPE format](https://cpe.mitre.org/specification/)\n\ + denoting the package manager version distributing a package." + version: + description: "The version installed at this location." + $ref: "#/definitions/packageVersion" + path: + type: "string" + description: "The path from which we gathered that this package/version is\ + \ installed." + description: "An occurrence of a particular package installation found within\ + \ a system's\nfilesystem. E.g., glibc was found in `/var/lib/dpkg/status`." + example: + path: "path" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + v1beta1provenanceArtifact: + type: "object" + properties: + checksum: + type: "string" + description: "Hash or checksum value of a binary, or Docker Registry 2.0 digest\ + \ of a\ncontainer." + id: + type: "string" + description: "Artifact ID, if any; for container images, this will be a URL\ + \ by digest\nlike `gcr.io/projectID/imagename@sha256:123456`." + names: + type: "array" + description: "Related artifact names. This may be the path to a binary or\ + \ jar file, or in\nthe case of a container build, the name used to push\ + \ the container image to\nGoogle Container Registry, as presented to `docker\ + \ push`. Note that a\nsingle Artifact ID can have multiple names, for example\ + \ if two tags are\napplied to one image." + items: + type: "string" + description: "Artifact describes a build product." + example: + names: + - "names" + - "names" + checksum: "checksum" + id: "id" + v1beta1vulnerabilityDetails: + type: "object" + properties: + type: + type: "string" + title: "The type of package; whether native or non native(ruby gems, node.js\n\ + packages etc)" + severity: + description: "Output only. The note provider assigned Severity of the vulnerability." + readOnly: true + $ref: "#/definitions/vulnerabilitySeverity" + cvss_score: + type: "number" + format: "float" + description: "Output only. The CVSS score of this vulnerability. CVSS score\ + \ is on a\nscale of 0-10 where 0 indicates low severity and 10 indicates\ + \ high\nseverity." + readOnly: true + package_issue: + type: "array" + description: "Required. The set of affected locations and their fixes (if\ + \ available)\nwithin the associated resource." + items: + $ref: "#/definitions/vulnerabilityPackageIssue" + short_description: + type: "string" + description: "Output only. A one sentence description of this vulnerability." + readOnly: true + long_description: + type: "string" + description: "Output only. A detailed description of this vulnerability." + readOnly: true + related_urls: + type: "array" + description: "Output only. URLs related to this vulnerability." + readOnly: true + items: + $ref: "#/definitions/v1beta1RelatedUrl" + effective_severity: + description: "The distro assigned severity for this vulnerability when it\ + \ is\navailable, and note provider assigned severity when distro has not\ + \ yet\nassigned a severity for this vulnerability." + $ref: "#/definitions/vulnerabilitySeverity" + description: "Details of a vulnerability Occurrence." + example: + severity: {} + short_description: "short_description" + cvss_score: 0.8008282 + long_description: "long_description" + related_urls: + - label: "label" + url: "url" + - label: "label" + url: "url" + type: "type" + package_issue: + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + vulnerabilityCVSSv3: + type: "object" + properties: + base_score: + type: "number" + format: "float" + description: "The base score is a function of the base metric scores." + exploitability_score: + type: "number" + format: "float" + impact_score: + type: "number" + format: "float" + attack_vector: + description: "Base Metrics\nRepresents the intrinsic characteristics of a\ + \ vulnerability that are\nconstant over time and across user environments." + $ref: "#/definitions/CVSSv3AttackVector" + attack_complexity: + $ref: "#/definitions/CVSSv3AttackComplexity" + privileges_required: + $ref: "#/definitions/CVSSv3PrivilegesRequired" + user_interaction: + $ref: "#/definitions/CVSSv3UserInteraction" + scope: + $ref: "#/definitions/CVSSv3Scope" + confidentiality_impact: + $ref: "#/definitions/CVSSv3Impact" + integrity_impact: + $ref: "#/definitions/CVSSv3Impact" + availability_impact: + $ref: "#/definitions/CVSSv3Impact" + title: "Common Vulnerability Scoring System version 3.\nFor details, see https://www.first.org/cvss/specification-document" + example: + attack_complexity: {} + base_score: 1.4658129 + user_interaction: {} + scope: {} + impact_score: 5.637377 + confidentiality_impact: {} + attack_vector: {} + exploitability_score: 5.962134 + privileges_required: {} + vulnerabilityPackageIssue: + type: "object" + properties: + affected_location: + description: "Required. The location of the vulnerability." + $ref: "#/definitions/vulnerabilityVulnerabilityLocation" + fixed_location: + description: "The location of the available fix for vulnerability." + $ref: "#/definitions/vulnerabilityVulnerabilityLocation" + severity_name: + type: "string" + description: "Deprecated, use Details.effective_severity instead\nThe severity\ + \ (e.g., distro assigned severity) for this vulnerability." + description: "This message wraps a location affected by a vulnerability and its\n\ + associated fix (if one is available)." + example: + fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + affected_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + vulnerabilitySeverity: + type: "string" + description: "Note provider-assigned severity/impact ranking.\n\n - SEVERITY_UNSPECIFIED:\ + \ Unknown.\n - MINIMAL: Minimal severity.\n - LOW: Low severity.\n - MEDIUM:\ + \ Medium severity.\n - HIGH: High severity.\n - CRITICAL: Critical severity." + enum: + - "SEVERITY_UNSPECIFIED" + - "MINIMAL" + - "LOW" + - "MEDIUM" + - "HIGH" + - "CRITICAL" + default: "SEVERITY_UNSPECIFIED" + vulnerabilityVulnerability: + type: "object" + properties: + cvss_score: + type: "number" + format: "float" + description: "The CVSS score for this vulnerability." + severity: + description: "Note provider assigned impact of the vulnerability." + $ref: "#/definitions/vulnerabilitySeverity" + details: + type: "array" + description: "All information about the package to specifically identify this\n\ + vulnerability. One entry per (version range and cpe_uri) the package\nvulnerability\ + \ has manifested in." + items: + $ref: "#/definitions/VulnerabilityDetail" + cvss_v3: + description: "The full description of the CVSSv3." + $ref: "#/definitions/vulnerabilityCVSSv3" + windows_details: + type: "array" + description: "Windows details get their own format because the information\ + \ format and\nmodel don't match a normal detail. Specifically Windows updates\ + \ are done as\npatches, thus Windows vulnerabilities really are a missing\ + \ package, rather\nthan a package being at an incorrect version." + items: + $ref: "#/definitions/VulnerabilityWindowsDetail" + source_update_time: + type: "string" + format: "date-time" + description: "The time this information was last changed at the source. This\ + \ is an\nupstream timestamp from the underlying information source - e.g.\ + \ Ubuntu\nsecurity tracker." + description: "Vulnerability provides metadata about a security vulnerability in\ + \ a Note." + example: + severity: {} + cvss_score: 0.8008282 + cvss_v3: + attack_complexity: {} + base_score: 1.4658129 + user_interaction: {} + scope: {} + impact_score: 5.637377 + confidentiality_impact: {} + attack_vector: {} + exploitability_score: 5.962134 + privileges_required: {} + details: + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + package: "package" + cpe_uri: "cpe_uri" + max_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + description: "description" + min_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + source_update_time: "2000-01-23T04:56:07.000+00:00" + package_type: "package_type" + is_obsolete: true + - fixed_location: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + package: "package" + cpe_uri: "cpe_uri" + max_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + description: "description" + min_affected_version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" + severity_name: "severity_name" + source_update_time: "2000-01-23T04:56:07.000+00:00" + package_type: "package_type" + is_obsolete: true + source_update_time: "2000-01-23T04:56:07.000+00:00" + windows_details: + - cpe_uri: "cpe_uri" + fixing_kbs: + - name: "name" + url: "url" + - name: "name" + url: "url" + name: "name" + description: "description" + - cpe_uri: "cpe_uri" + fixing_kbs: + - name: "name" + url: "url" + - name: "name" + url: "url" + name: "name" + description: "description" + vulnerabilityVulnerabilityLocation: + type: "object" + properties: + cpe_uri: + type: "string" + description: "Required. The CPE URI in [cpe format](https://cpe.mitre.org/specification/)\n\ + format. Examples include distro or storage location for vulnerable jar." + package: + type: "string" + description: "Required. The package being described." + version: + description: "Required. The version of the package being described." + $ref: "#/definitions/packageVersion" + description: "The location of the vulnerability." + example: + package: "package" + cpe_uri: "cpe_uri" + version: + kind: {} + name: "name" + epoch: 6 + revision: "revision" diff --git a/image-scanner/grafeas/api_grafeas_v1_beta1.go b/image-scanner/grafeas/api_grafeas_v1_beta1.go new file mode 100644 index 000000000..8a1ed2e5b --- /dev/null +++ b/image-scanner/grafeas/api_grafeas_v1_beta1.go @@ -0,0 +1,1451 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +import ( + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" + "fmt" + "github.com/antihax/optional" +) + +// Linger please +var ( + _ context.Context +) + +type GrafeasV1Beta1ApiService service + +/* +GrafeasV1Beta1ApiService Creates new notes in batch. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param parent The name of the project in the form of `projects/[PROJECT_ID]`, under which the notes are to be created. + * @param body + +@return V1beta1BatchCreateNotesResponse +*/ +func (a *GrafeasV1Beta1ApiService) BatchCreateNotes(ctx context.Context, parent string, body V1beta1BatchCreateNotesRequest) (V1beta1BatchCreateNotesResponse, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue V1beta1BatchCreateNotesResponse + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{parent=projects/*}/notes:batchCreate" + localVarPath = strings.Replace(localVarPath, "{"+"parent"+"}", fmt.Sprintf("%v", parent), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &body + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v V1beta1BatchCreateNotesResponse + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +GrafeasV1Beta1ApiService Creates new occurrences in batch. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param parent The name of the project in the form of `projects/[PROJECT_ID]`, under which the occurrences are to be created. + * @param body + +@return V1beta1BatchCreateOccurrencesResponse +*/ +func (a *GrafeasV1Beta1ApiService) BatchCreateOccurrences(ctx context.Context, parent string, body V1beta1BatchCreateOccurrencesRequest) (V1beta1BatchCreateOccurrencesResponse, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue V1beta1BatchCreateOccurrencesResponse + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{parent=projects/*}/occurrences:batchCreate" + localVarPath = strings.Replace(localVarPath, "{"+"parent"+"}", fmt.Sprintf("%v", parent), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &body + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v V1beta1BatchCreateOccurrencesResponse + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +GrafeasV1Beta1ApiService Creates a new note. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param parent The name of the project in the form of `projects/[PROJECT_ID]`, under which the note is to be created. + * @param body The note to create. + +@return V1beta1Note +*/ +func (a *GrafeasV1Beta1ApiService) CreateNote(ctx context.Context, parent string, body V1beta1Note) (V1beta1Note, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue V1beta1Note + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{parent=projects/*}/notes" + localVarPath = strings.Replace(localVarPath, "{"+"parent"+"}", fmt.Sprintf("%v", parent), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &body + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v V1beta1Note + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +GrafeasV1Beta1ApiService Creates a new occurrence. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param parent The name of the project in the form of `projects/[PROJECT_ID]`, under which the occurrence is to be created. + * @param body The occurrence to create. + +@return V1beta1Occurrence +*/ +func (a *GrafeasV1Beta1ApiService) CreateOccurrence(ctx context.Context, parent string, body V1beta1Occurrence) (V1beta1Occurrence, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Post") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue V1beta1Occurrence + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{parent=projects/*}/occurrences" + localVarPath = strings.Replace(localVarPath, "{"+"parent"+"}", fmt.Sprintf("%v", parent), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &body + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v V1beta1Occurrence + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +GrafeasV1Beta1ApiService Deletes the specified note. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param name The name of the note in the form of `projects/[PROVIDER_ID]/notes/[NOTE_ID]`. + +@return interface{} +*/ +func (a *GrafeasV1Beta1ApiService) DeleteNote(ctx context.Context, name string) (interface{}, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Delete") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue interface{} + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{name=projects/*/notes/*}" + localVarPath = strings.Replace(localVarPath, "{"+"name"+"}", fmt.Sprintf("%v", name), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v interface{} + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +GrafeasV1Beta1ApiService Deletes the specified occurrence. For example, use this method to delete an occurrence when the occurrence is no longer applicable for the given resource. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param name The name of the occurrence in the form of `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`. + +@return interface{} +*/ +func (a *GrafeasV1Beta1ApiService) DeleteOccurrence(ctx context.Context, name string) (interface{}, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Delete") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue interface{} + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{name=projects/*/occurrences/*}" + localVarPath = strings.Replace(localVarPath, "{"+"name"+"}", fmt.Sprintf("%v", name), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v interface{} + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +GrafeasV1Beta1ApiService Gets the specified note. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param name The name of the note in the form of `projects/[PROVIDER_ID]/notes/[NOTE_ID]`. + +@return V1beta1Note +*/ +func (a *GrafeasV1Beta1ApiService) GetNote(ctx context.Context, name string) (V1beta1Note, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue V1beta1Note + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{name=projects/*/notes/*}" + localVarPath = strings.Replace(localVarPath, "{"+"name"+"}", fmt.Sprintf("%v", name), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v V1beta1Note + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +GrafeasV1Beta1ApiService Gets the specified occurrence. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param name The name of the occurrence in the form of `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`. + +@return V1beta1Occurrence +*/ +func (a *GrafeasV1Beta1ApiService) GetOccurrence(ctx context.Context, name string) (V1beta1Occurrence, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue V1beta1Occurrence + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{name=projects/*/occurrences/*}" + localVarPath = strings.Replace(localVarPath, "{"+"name"+"}", fmt.Sprintf("%v", name), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v V1beta1Occurrence + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +GrafeasV1Beta1ApiService Gets the note attached to the specified occurrence. Consumer projects can use this method to get a note that belongs to a provider project. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param name The name of the occurrence in the form of `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`. + +@return V1beta1Note +*/ +func (a *GrafeasV1Beta1ApiService) GetOccurrenceNote(ctx context.Context, name string) (V1beta1Note, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue V1beta1Note + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{name=projects/*/occurrences/*}/notes" + localVarPath = strings.Replace(localVarPath, "{"+"name"+"}", fmt.Sprintf("%v", name), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v V1beta1Note + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +GrafeasV1Beta1ApiService Gets a summary of the number and severity of occurrences. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param parent The name of the project to get a vulnerability summary for in the form of `projects/[PROJECT_ID]`. + * @param optional nil or *GetVulnerabilityOccurrencesSummaryOpts - Optional Parameters: + * @param "Filter" (optional.String) - The filter expression. + +@return V1beta1VulnerabilityOccurrencesSummary +*/ + +type GetVulnerabilityOccurrencesSummaryOpts struct { + Filter optional.String +} + +func (a *GrafeasV1Beta1ApiService) GetVulnerabilityOccurrencesSummary(ctx context.Context, parent string, localVarOptionals *GetVulnerabilityOccurrencesSummaryOpts) (V1beta1VulnerabilityOccurrencesSummary, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue V1beta1VulnerabilityOccurrencesSummary + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{parent=projects/*}/occurrences:vulnerabilitySummary" + localVarPath = strings.Replace(localVarPath, "{"+"parent"+"}", fmt.Sprintf("%v", parent), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if localVarOptionals != nil && localVarOptionals.Filter.IsSet() { + localVarQueryParams.Add("filter", parameterToString(localVarOptionals.Filter.Value(), "")) + } + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v V1beta1VulnerabilityOccurrencesSummary + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +GrafeasV1Beta1ApiService Lists occurrences referencing the specified note. Provider projects can use this method to get all occurrences across consumer projects referencing the specified note. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param name The name of the note to list occurrences for in the form of `projects/[PROVIDER_ID]/notes/[NOTE_ID]`. + * @param optional nil or *ListNoteOccurrencesOpts - Optional Parameters: + * @param "Filter" (optional.String) - The filter expression. + * @param "PageSize" (optional.Int32) - Number of occurrences to return in the list. + * @param "PageToken" (optional.String) - Token to provide to skip to a particular spot in the list. + +@return V1beta1ListNoteOccurrencesResponse +*/ + +type ListNoteOccurrencesOpts struct { + Filter optional.String + PageSize optional.Int32 + PageToken optional.String +} + +func (a *GrafeasV1Beta1ApiService) ListNoteOccurrences(ctx context.Context, name string, localVarOptionals *ListNoteOccurrencesOpts) (V1beta1ListNoteOccurrencesResponse, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue V1beta1ListNoteOccurrencesResponse + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{name=projects/*/notes/*}/occurrences" + localVarPath = strings.Replace(localVarPath, "{"+"name"+"}", fmt.Sprintf("%v", name), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if localVarOptionals != nil && localVarOptionals.Filter.IsSet() { + localVarQueryParams.Add("filter", parameterToString(localVarOptionals.Filter.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.PageSize.IsSet() { + localVarQueryParams.Add("page_size", parameterToString(localVarOptionals.PageSize.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.PageToken.IsSet() { + localVarQueryParams.Add("page_token", parameterToString(localVarOptionals.PageToken.Value(), "")) + } + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v V1beta1ListNoteOccurrencesResponse + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +GrafeasV1Beta1ApiService Lists notes for the specified project. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param parent The name of the project to list notes for in the form of `projects/[PROJECT_ID]`. + * @param optional nil or *ListNotesOpts - Optional Parameters: + * @param "Filter" (optional.String) - The filter expression. + * @param "PageSize" (optional.Int32) - Number of notes to return in the list. Must be positive. Max allowed page size is 1000. If not specified, page size defaults to 20. + * @param "PageToken" (optional.String) - Token to provide to skip to a particular spot in the list. + +@return V1beta1ListNotesResponse +*/ + +type ListNotesOpts struct { + Filter optional.String + PageSize optional.Int32 + PageToken optional.String +} + +func (a *GrafeasV1Beta1ApiService) ListNotes(ctx context.Context, parent string, localVarOptionals *ListNotesOpts) (V1beta1ListNotesResponse, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue V1beta1ListNotesResponse + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{parent=projects/*}/notes" + localVarPath = strings.Replace(localVarPath, "{"+"parent"+"}", fmt.Sprintf("%v", parent), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if localVarOptionals != nil && localVarOptionals.Filter.IsSet() { + localVarQueryParams.Add("filter", parameterToString(localVarOptionals.Filter.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.PageSize.IsSet() { + localVarQueryParams.Add("page_size", parameterToString(localVarOptionals.PageSize.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.PageToken.IsSet() { + localVarQueryParams.Add("page_token", parameterToString(localVarOptionals.PageToken.Value(), "")) + } + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v V1beta1ListNotesResponse + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +GrafeasV1Beta1ApiService Lists occurrences for the specified project. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param parent The name of the project to list occurrences for in the form of `projects/[PROJECT_ID]`. + * @param optional nil or *ListOccurrencesOpts - Optional Parameters: + * @param "Filter" (optional.String) - The filter expression. + * @param "PageSize" (optional.Int32) - Number of occurrences to return in the list. Must be positive. Max allowed page size is 1000. If not specified, page size defaults to 20. + * @param "PageToken" (optional.String) - Token to provide to skip to a particular spot in the list. + +@return V1beta1ListOccurrencesResponse +*/ + +type ListOccurrencesOpts struct { + Filter optional.String + PageSize optional.Int32 + PageToken optional.String +} + +func (a *GrafeasV1Beta1ApiService) ListOccurrences(ctx context.Context, parent string, localVarOptionals *ListOccurrencesOpts) (V1beta1ListOccurrencesResponse, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Get") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue V1beta1ListOccurrencesResponse + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{parent=projects/*}/occurrences" + localVarPath = strings.Replace(localVarPath, "{"+"parent"+"}", fmt.Sprintf("%v", parent), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if localVarOptionals != nil && localVarOptionals.Filter.IsSet() { + localVarQueryParams.Add("filter", parameterToString(localVarOptionals.Filter.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.PageSize.IsSet() { + localVarQueryParams.Add("page_size", parameterToString(localVarOptionals.PageSize.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.PageToken.IsSet() { + localVarQueryParams.Add("page_token", parameterToString(localVarOptionals.PageToken.Value(), "")) + } + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v V1beta1ListOccurrencesResponse + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +GrafeasV1Beta1ApiService Updates the specified note. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param name The name of the note in the form of `projects/[PROVIDER_ID]/notes/[NOTE_ID]`. + * @param body The updated note. + +@return V1beta1Note +*/ +func (a *GrafeasV1Beta1ApiService) UpdateNote(ctx context.Context, name string, body V1beta1Note) (V1beta1Note, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Patch") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue V1beta1Note + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{name=projects/*/notes/*}" + localVarPath = strings.Replace(localVarPath, "{"+"name"+"}", fmt.Sprintf("%v", name), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &body + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v V1beta1Note + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} + +/* +GrafeasV1Beta1ApiService Updates the specified occurrence. + * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param name The name of the occurrence in the form of `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`. + * @param body The updated occurrence. + +@return V1beta1Occurrence +*/ +func (a *GrafeasV1Beta1ApiService) UpdateOccurrence(ctx context.Context, name string, body V1beta1Occurrence) (V1beta1Occurrence, *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("Patch") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte + localVarReturnValue V1beta1Occurrence + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/v1beta1/{name=projects/*/occurrences/*}" + localVarPath = strings.Replace(localVarPath, "{"+"name"+"}", fmt.Sprintf("%v", name), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHttpContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) + if localVarHttpContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHttpContentType + } + + // to determine the Accept header + localVarHttpHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) + if localVarHttpHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHttpHeaderAccept + } + // body params + localVarPostBody = &body + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return localVarReturnValue, localVarHttpResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) + localVarHttpResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHttpResponse, err + } + + if localVarHttpResponse.StatusCode < 300 { + // If we succeed, return the data, otherwise pass on to decode error. + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err == nil { + return localVarReturnValue, localVarHttpResponse, err + } + } + + if localVarHttpResponse.StatusCode >= 300 { + newErr := GenericSwaggerError{ + body: localVarBody, + error: localVarHttpResponse.Status, + } + + if localVarHttpResponse.StatusCode == 200 { + var v V1beta1Occurrence + err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHttpResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, newErr + } + + return localVarReturnValue, localVarHttpResponse, nil +} diff --git a/image-scanner/grafeas/client.go b/image-scanner/grafeas/client.go new file mode 100644 index 000000000..581f6ad4a --- /dev/null +++ b/image-scanner/grafeas/client.go @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +import ( + "bytes" + "context" + "encoding/json" + "encoding/xml" + "errors" + "fmt" + "io" + "mime/multipart" + "net/http" + "net/url" + "os" + "path/filepath" + "reflect" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" + + "golang.org/x/oauth2" +) + +var ( + jsonCheck = regexp.MustCompile("(?i:[application|text]/json)") + xmlCheck = regexp.MustCompile("(?i:[application|text]/xml)") +) + +// APIClient manages communication with the grafeas.proto API vversion not set +// In most cases there should be only one, shared, APIClient. +type APIClient struct { + cfg *Configuration + common service // Reuse a single struct instead of allocating one for each service on the heap. + + // API Services + + GrafeasV1Beta1Api *GrafeasV1Beta1ApiService +} + +type service struct { + client *APIClient +} + +// NewAPIClient creates a new API client. Requires a userAgent string describing your application. +// optionally a custom http.Client to allow for advanced features such as caching. +func NewAPIClient(cfg *Configuration) *APIClient { + if cfg.HTTPClient == nil { + cfg.HTTPClient = http.DefaultClient + } + + c := &APIClient{} + c.cfg = cfg + c.common.client = c + + // API Services + c.GrafeasV1Beta1Api = (*GrafeasV1Beta1ApiService)(&c.common) + + return c +} + +func atoi(in string) (int, error) { + return strconv.Atoi(in) +} + +// selectHeaderContentType select a content type from the available list. +func selectHeaderContentType(contentTypes []string) string { + if len(contentTypes) == 0 { + return "" + } + if contains(contentTypes, "application/json") { + return "application/json" + } + return contentTypes[0] // use the first content type specified in 'consumes' +} + +// selectHeaderAccept join all accept types and return +func selectHeaderAccept(accepts []string) string { + if len(accepts) == 0 { + return "" + } + + if contains(accepts, "application/json") { + return "application/json" + } + + return strings.Join(accepts, ",") +} + +// contains is a case insenstive match, finding needle in a haystack +func contains(haystack []string, needle string) bool { + for _, a := range haystack { + if strings.ToLower(a) == strings.ToLower(needle) { + return true + } + } + return false +} + +// Verify optional parameters are of the correct type. +func typeCheckParameter(obj interface{}, expected string, name string) error { + // Make sure there is an object. + if obj == nil { + return nil + } + + // Check the type is as expected. + if reflect.TypeOf(obj).String() != expected { + return fmt.Errorf("Expected %s to be of type %s but received %s.", name, expected, reflect.TypeOf(obj).String()) + } + return nil +} + +// parameterToString convert interface{} parameters to string, using a delimiter if format is provided. +func parameterToString(obj interface{}, collectionFormat string) string { + var delimiter string + + switch collectionFormat { + case "pipes": + delimiter = "|" + case "ssv": + delimiter = " " + case "tsv": + delimiter = "\t" + case "csv": + delimiter = "," + } + + if reflect.TypeOf(obj).Kind() == reflect.Slice { + return strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]") + } + + return fmt.Sprintf("%v", obj) +} + +// callAPI do the request. +func (c *APIClient) callAPI(request *http.Request) (*http.Response, error) { + return c.cfg.HTTPClient.Do(request) +} + +// Change base path to allow switching to mocks +func (c *APIClient) ChangeBasePath(path string) { + c.cfg.BasePath = path +} + +// prepareRequest build the request +func (c *APIClient) prepareRequest( + ctx context.Context, + path string, method string, + postBody interface{}, + headerParams map[string]string, + queryParams url.Values, + formParams url.Values, + fileName string, + fileBytes []byte) (localVarRequest *http.Request, err error) { + + var body *bytes.Buffer + + // Detect postBody type and post. + if postBody != nil { + contentType := headerParams["Content-Type"] + if contentType == "" { + contentType = detectContentType(postBody) + headerParams["Content-Type"] = contentType + } + + body, err = setBody(postBody, contentType) + if err != nil { + return nil, err + } + } + + // add form parameters and file if available. + if len(formParams) > 0 || (len(fileBytes) > 0 && fileName != "") { + if body != nil { + return nil, errors.New("Cannot specify postBody and multipart form at the same time.") + } + body = &bytes.Buffer{} + w := multipart.NewWriter(body) + + for k, v := range formParams { + for _, iv := range v { + if strings.HasPrefix(k, "@") { // file + err = addFile(w, k[1:], iv) + if err != nil { + return nil, err + } + } else { // form value + w.WriteField(k, iv) + } + } + } + if len(fileBytes) > 0 && fileName != "" { + w.Boundary() + //_, fileNm := filepath.Split(fileName) + part, err := w.CreateFormFile("file", filepath.Base(fileName)) + if err != nil { + return nil, err + } + _, err = part.Write(fileBytes) + if err != nil { + return nil, err + } + // Set the Boundary in the Content-Type + headerParams["Content-Type"] = w.FormDataContentType() + } + + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + w.Close() + } + + // Setup path and query parameters + url, err := url.Parse(path) + if err != nil { + return nil, err + } + + // Adding Query Param + query := url.Query() + for k, v := range queryParams { + for _, iv := range v { + query.Add(k, iv) + } + } + + // Encode the parameters. + url.RawQuery = query.Encode() + + // Generate a new request + if body != nil { + localVarRequest, err = http.NewRequest(method, url.String(), body) + } else { + localVarRequest, err = http.NewRequest(method, url.String(), nil) + } + if err != nil { + return nil, err + } + + // add header parameters, if any + if len(headerParams) > 0 { + headers := http.Header{} + for h, v := range headerParams { + headers.Set(h, v) + } + localVarRequest.Header = headers + } + + // Override request host, if applicable + if c.cfg.Host != "" { + localVarRequest.Host = c.cfg.Host + } + + // Add the user agent to the request. + localVarRequest.Header.Add("User-Agent", c.cfg.UserAgent) + + if ctx != nil { + // add context to the request + localVarRequest = localVarRequest.WithContext(ctx) + + // Walk through any authentication. + + // OAuth2 authentication + if tok, ok := ctx.Value(ContextOAuth2).(oauth2.TokenSource); ok { + // We were able to grab an oauth2 token from the context + var latestToken *oauth2.Token + if latestToken, err = tok.Token(); err != nil { + return nil, err + } + + latestToken.SetAuthHeader(localVarRequest) + } + + // Basic HTTP Authentication + if auth, ok := ctx.Value(ContextBasicAuth).(BasicAuth); ok { + localVarRequest.SetBasicAuth(auth.UserName, auth.Password) + } + + // AccessToken Authentication + if auth, ok := ctx.Value(ContextAccessToken).(string); ok { + localVarRequest.Header.Add("Authorization", "Bearer "+auth) + } + } + + for header, value := range c.cfg.DefaultHeader { + localVarRequest.Header.Add(header, value) + } + + return localVarRequest, nil +} + +func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err error) { + if strings.Contains(contentType, "application/xml") { + if err = xml.Unmarshal(b, v); err != nil { + return err + } + return nil + } else if strings.Contains(contentType, "application/json") { + if err = json.Unmarshal(b, v); err != nil { + return err + } + return nil + } + return errors.New("undefined response type") +} + +// Add a file to the multipart request +func addFile(w *multipart.Writer, fieldName, path string) error { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + part, err := w.CreateFormFile(fieldName, filepath.Base(path)) + if err != nil { + return err + } + _, err = io.Copy(part, file) + + return err +} + +// Prevent trying to import "fmt" +func reportError(format string, a ...interface{}) error { + return fmt.Errorf(format, a...) +} + +// Set request body from an interface{} +func setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) { + if bodyBuf == nil { + bodyBuf = &bytes.Buffer{} + } + + if reader, ok := body.(io.Reader); ok { + _, err = bodyBuf.ReadFrom(reader) + } else if b, ok := body.([]byte); ok { + _, err = bodyBuf.Write(b) + } else if s, ok := body.(string); ok { + _, err = bodyBuf.WriteString(s) + } else if s, ok := body.(*string); ok { + _, err = bodyBuf.WriteString(*s) + } else if jsonCheck.MatchString(contentType) { + err = json.NewEncoder(bodyBuf).Encode(body) + } else if xmlCheck.MatchString(contentType) { + xml.NewEncoder(bodyBuf).Encode(body) + } + + if err != nil { + return nil, err + } + + if bodyBuf.Len() == 0 { + err = fmt.Errorf("Invalid body type %s\n", contentType) + return nil, err + } + return bodyBuf, nil +} + +// detectContentType method is used to figure out `Request.Body` content type for request header +func detectContentType(body interface{}) string { + contentType := "text/plain; charset=utf-8" + kind := reflect.TypeOf(body).Kind() + + switch kind { + case reflect.Struct, reflect.Map, reflect.Ptr: + contentType = "application/json; charset=utf-8" + case reflect.String: + contentType = "text/plain; charset=utf-8" + default: + if b, ok := body.([]byte); ok { + contentType = http.DetectContentType(b) + } else if kind == reflect.Slice { + contentType = "application/json; charset=utf-8" + } + } + + return contentType +} + +// Ripped from https://github.com/gregjones/httpcache/blob/master/httpcache.go +type cacheControl map[string]string + +func parseCacheControl(headers http.Header) cacheControl { + cc := cacheControl{} + ccHeader := headers.Get("Cache-Control") + for _, part := range strings.Split(ccHeader, ",") { + part = strings.Trim(part, " ") + if part == "" { + continue + } + if strings.ContainsRune(part, '=') { + keyval := strings.Split(part, "=") + cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",") + } else { + cc[part] = "" + } + } + return cc +} + +// CacheExpires helper function to determine remaining time before repeating a request. +func CacheExpires(r *http.Response) time.Time { + // Figure out when the cache expires. + var expires time.Time + now, err := time.Parse(time.RFC1123, r.Header.Get("date")) + if err != nil { + return time.Now() + } + respCacheControl := parseCacheControl(r.Header) + + if maxAge, ok := respCacheControl["max-age"]; ok { + lifetime, err := time.ParseDuration(maxAge + "s") + if err != nil { + expires = now + } + expires = now.Add(lifetime) + } else { + expiresHeader := r.Header.Get("Expires") + if expiresHeader != "" { + expires, err = time.Parse(time.RFC1123, expiresHeader) + if err != nil { + expires = now + } + } + } + return expires +} + +func strlen(s string) int { + return utf8.RuneCountInString(s) +} + +// GenericSwaggerError Provides access to the body, error and model on returned errors. +type GenericSwaggerError struct { + body []byte + error string + model interface{} +} + +// Error returns non-empty string if there was an error. +func (e GenericSwaggerError) Error() string { + return e.error +} + +// Body returns the raw bytes of the response +func (e GenericSwaggerError) Body() []byte { + return e.body +} + +// Model returns the unpacked model of the error +func (e GenericSwaggerError) Model() interface{} { + return e.model +} \ No newline at end of file diff --git a/image-scanner/grafeas/configuration.go b/image-scanner/grafeas/configuration.go new file mode 100644 index 000000000..3fff93139 --- /dev/null +++ b/image-scanner/grafeas/configuration.go @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +import ( + "net/http" +) + +// contextKeys are used to identify the type of value in the context. +// Since these are string, it is possible to get a short description of the +// context key for logging and debugging using key.String(). + +type contextKey string + +func (c contextKey) String() string { + return "auth " + string(c) +} + +var ( + // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request. + ContextOAuth2 = contextKey("token") + + // ContextBasicAuth takes BasicAuth as authentication for the request. + ContextBasicAuth = contextKey("basic") + + // ContextAccessToken takes a string oauth2 access token as authentication for the request. + ContextAccessToken = contextKey("accesstoken") + + // ContextAPIKey takes an APIKey as authentication for the request + ContextAPIKey = contextKey("apikey") +) + +// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth +type BasicAuth struct { + UserName string `json:"userName,omitempty"` + Password string `json:"password,omitempty"` +} + +// APIKey provides API key based authentication to a request passed via context using ContextAPIKey +type APIKey struct { + Key string + Prefix string +} + +type Configuration struct { + BasePath string `json:"basePath,omitempty"` + Host string `json:"host,omitempty"` + Scheme string `json:"scheme,omitempty"` + DefaultHeader map[string]string `json:"defaultHeader,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + HTTPClient *http.Client +} + +func NewConfiguration() *Configuration { + cfg := &Configuration{ + BasePath: "http://localhost", + DefaultHeader: make(map[string]string), + UserAgent: "Swagger-Codegen/0.1.4/go", + } + return cfg +} + +func (c *Configuration) AddDefaultHeader(key string, value string) { + c.DefaultHeader[key] = value +} diff --git a/image-scanner/grafeas/docs/AliasContextKind.md b/image-scanner/grafeas/docs/AliasContextKind.md new file mode 100644 index 000000000..78893df25 --- /dev/null +++ b/image-scanner/grafeas/docs/AliasContextKind.md @@ -0,0 +1,9 @@ +# AliasContextKind + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/AttestationAttestation.md b/image-scanner/grafeas/docs/AttestationAttestation.md new file mode 100644 index 000000000..c8321209e --- /dev/null +++ b/image-scanner/grafeas/docs/AttestationAttestation.md @@ -0,0 +1,11 @@ +# AttestationAttestation + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**PgpSignedAttestation** | [***AttestationPgpSignedAttestation**](attestationPgpSignedAttestation.md) | A PGP signed attestation. | [optional] [default to null] +**GenericSignedAttestation** | [***AttestationGenericSignedAttestation**](attestationGenericSignedAttestation.md) | An attestation that supports multiple `Signature`s over the same attestation payload. The signatures (defined in common.proto) support a superset of public key types and IDs compared to PgpSignedAttestation. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/AttestationAuthority.md b/image-scanner/grafeas/docs/AttestationAuthority.md new file mode 100644 index 000000000..437d26f76 --- /dev/null +++ b/image-scanner/grafeas/docs/AttestationAuthority.md @@ -0,0 +1,10 @@ +# AttestationAuthority + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Hint** | [***AuthorityHint**](AuthorityHint.md) | Hint hints at the purpose of the attestation authority. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/AttestationGenericSignedAttestation.md b/image-scanner/grafeas/docs/AttestationGenericSignedAttestation.md new file mode 100644 index 000000000..a833299bb --- /dev/null +++ b/image-scanner/grafeas/docs/AttestationGenericSignedAttestation.md @@ -0,0 +1,12 @@ +# AttestationGenericSignedAttestation + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ContentType** | [***AttestationGenericSignedAttestationContentType**](attestationGenericSignedAttestationContentType.md) | Type (for example schema) of the attestation payload that was signed. The verifier must ensure that the provided type is one that the verifier supports, and that the attestation payload is a valid instantiation of that type (for example by validating a JSON schema). | [optional] [default to null] +**SerializedPayload** | **string** | The serialized payload that is verified by one or more `signatures`. The encoding and semantic meaning of this payload must match what is set in `content_type`. | [optional] [default to null] +**Signatures** | [**[]Grafeasv1beta1Signature**](grafeasv1beta1Signature.md) | One or more signatures over `serialized_payload`. Verifier implementations should consider this attestation message verified if at least one `signature` verifies `serialized_payload`. See `Signature` in common.proto for more details on signature structure and verification. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/AttestationGenericSignedAttestationContentType.md b/image-scanner/grafeas/docs/AttestationGenericSignedAttestationContentType.md new file mode 100644 index 000000000..8c358b6f6 --- /dev/null +++ b/image-scanner/grafeas/docs/AttestationGenericSignedAttestationContentType.md @@ -0,0 +1,9 @@ +# AttestationGenericSignedAttestationContentType + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/AttestationPgpSignedAttestation.md b/image-scanner/grafeas/docs/AttestationPgpSignedAttestation.md new file mode 100644 index 000000000..6d270e5d3 --- /dev/null +++ b/image-scanner/grafeas/docs/AttestationPgpSignedAttestation.md @@ -0,0 +1,12 @@ +# AttestationPgpSignedAttestation + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Signature** | **string** | Required. The raw content of the signature, as output by GNU Privacy Guard (GPG) or equivalent. Since this message only supports attached signatures, the payload that was signed must be attached. While the signature format supported is dependent on the verification implementation, currently only ASCII-armored (`--armor` to gpg), non-clearsigned (`--sign` rather than `--clearsign` to gpg) are supported. Concretely, `gpg --sign --armor --output=signature.gpg payload.json` will create the signature content expected in this field in `signature.gpg` for the `payload.json` attestation payload. | [optional] [default to null] +**ContentType** | [***AttestationPgpSignedAttestationContentType**](attestationPgpSignedAttestationContentType.md) | Type (for example schema) of the attestation payload that was signed. The verifier must ensure that the provided type is one that the verifier supports, and that the attestation payload is a valid instantiation of that type (for example by validating a JSON schema). | [optional] [default to null] +**PgpKeyId** | **string** | The cryptographic fingerprint of the key used to generate the signature, as output by, e.g. `gpg --list-keys`. This should be the version 4, full 160-bit fingerprint, expressed as a 40 character hexidecimal string. See https://tools.ietf.org/html/rfc4880#section-12.2 for details. Implementations may choose to acknowledge \"LONG\", \"SHORT\", or other abbreviated key IDs, but only the full fingerprint is guaranteed to work. In gpg, the full fingerprint can be retrieved from the `fpr` field returned when calling --list-keys with --with-colons. For example: ``` gpg --with-colons --with-fingerprint --force-v4-certs \\ --list-keys attester@example.com tru::1:1513631572:0:3:1:5 pub:...<SNIP>... fpr:::::::::24FF6481B76AC91E66A00AC657A93A81EF3AE6FB: ``` Above, the fingerprint is `24FF6481B76AC91E66A00AC657A93A81EF3AE6FB`. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/AttestationPgpSignedAttestationContentType.md b/image-scanner/grafeas/docs/AttestationPgpSignedAttestationContentType.md new file mode 100644 index 000000000..5c1320d61 --- /dev/null +++ b/image-scanner/grafeas/docs/AttestationPgpSignedAttestationContentType.md @@ -0,0 +1,9 @@ +# AttestationPgpSignedAttestationContentType + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/AuthorityHint.md b/image-scanner/grafeas/docs/AuthorityHint.md new file mode 100644 index 000000000..71ac518c4 --- /dev/null +++ b/image-scanner/grafeas/docs/AuthorityHint.md @@ -0,0 +1,10 @@ +# AuthorityHint + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**HumanReadableName** | **string** | Required. The human readable name of this attestation authority, for example \"qa\". | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/BuildBuild.md b/image-scanner/grafeas/docs/BuildBuild.md new file mode 100644 index 000000000..5ba54df48 --- /dev/null +++ b/image-scanner/grafeas/docs/BuildBuild.md @@ -0,0 +1,11 @@ +# BuildBuild + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**BuilderVersion** | **string** | Required. Immutable. Version of the builder which produced this build. | [optional] [default to null] +**Signature** | [***BuildBuildSignature**](buildBuildSignature.md) | Signature of the build in occurrences pointing to this build note containing build details. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/BuildBuildSignature.md b/image-scanner/grafeas/docs/BuildBuildSignature.md new file mode 100644 index 000000000..375e7557c --- /dev/null +++ b/image-scanner/grafeas/docs/BuildBuildSignature.md @@ -0,0 +1,13 @@ +# BuildBuildSignature + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**PublicKey** | **string** | Public key of the builder which can be used to verify that the related findings are valid and unchanged. If `key_type` is empty, this defaults to PEM encoded public keys. This field may be empty if `key_id` references an external key. For Cloud Build based signatures, this is a PEM encoded public key. To verify the Cloud Build signature, place the contents of this field into a file (public.pem). The signature field is base64-decoded into its binary representation in signature.bin, and the provenance bytes from `BuildDetails` are base64-decoded into a binary representation in signed.bin. OpenSSL can then verify the signature: `openssl sha256 -verify public.pem -signature signature.bin signed.bin` | [optional] [default to null] +**Signature** | **string** | Required. Signature of the related `BuildProvenance`. In JSON, this is base-64 encoded. | [optional] [default to null] +**KeyId** | **string** | An ID for the key used to sign. This could be either an ID for the key stored in `public_key` (such as the ID or fingerprint for a PGP key, or the CN for a cert), or a reference to an external key (such as a reference to a key in Cloud Key Management Service). | [optional] [default to null] +**KeyType** | [***BuildSignatureKeyType**](BuildSignatureKeyType.md) | The type of the key, either stored in `public_key` or referenced in `key_id`. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/BuildSignatureKeyType.md b/image-scanner/grafeas/docs/BuildSignatureKeyType.md new file mode 100644 index 000000000..c422692df --- /dev/null +++ b/image-scanner/grafeas/docs/BuildSignatureKeyType.md @@ -0,0 +1,9 @@ +# BuildSignatureKeyType + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/CvsSv3AttackComplexity.md b/image-scanner/grafeas/docs/CvsSv3AttackComplexity.md new file mode 100644 index 000000000..7855c5e9b --- /dev/null +++ b/image-scanner/grafeas/docs/CvsSv3AttackComplexity.md @@ -0,0 +1,9 @@ +# CvsSv3AttackComplexity + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/CvsSv3AttackVector.md b/image-scanner/grafeas/docs/CvsSv3AttackVector.md new file mode 100644 index 000000000..907e3db8b --- /dev/null +++ b/image-scanner/grafeas/docs/CvsSv3AttackVector.md @@ -0,0 +1,9 @@ +# CvsSv3AttackVector + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/CvsSv3Impact.md b/image-scanner/grafeas/docs/CvsSv3Impact.md new file mode 100644 index 000000000..8575beee0 --- /dev/null +++ b/image-scanner/grafeas/docs/CvsSv3Impact.md @@ -0,0 +1,9 @@ +# CvsSv3Impact + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/CvsSv3PrivilegesRequired.md b/image-scanner/grafeas/docs/CvsSv3PrivilegesRequired.md new file mode 100644 index 000000000..07bf90f87 --- /dev/null +++ b/image-scanner/grafeas/docs/CvsSv3PrivilegesRequired.md @@ -0,0 +1,9 @@ +# CvsSv3PrivilegesRequired + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/CvsSv3Scope.md b/image-scanner/grafeas/docs/CvsSv3Scope.md new file mode 100644 index 000000000..df2990d2b --- /dev/null +++ b/image-scanner/grafeas/docs/CvsSv3Scope.md @@ -0,0 +1,9 @@ +# CvsSv3Scope + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/CvsSv3UserInteraction.md b/image-scanner/grafeas/docs/CvsSv3UserInteraction.md new file mode 100644 index 000000000..a10c78114 --- /dev/null +++ b/image-scanner/grafeas/docs/CvsSv3UserInteraction.md @@ -0,0 +1,9 @@ +# CvsSv3UserInteraction + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/DeploymentDeployable.md b/image-scanner/grafeas/docs/DeploymentDeployable.md new file mode 100644 index 000000000..1ae869d1b --- /dev/null +++ b/image-scanner/grafeas/docs/DeploymentDeployable.md @@ -0,0 +1,10 @@ +# DeploymentDeployable + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ResourceUri** | **[]string** | Required. Resource URI for the artifact being deployed. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/DeploymentDeployment.md b/image-scanner/grafeas/docs/DeploymentDeployment.md new file mode 100644 index 000000000..901e9f47e --- /dev/null +++ b/image-scanner/grafeas/docs/DeploymentDeployment.md @@ -0,0 +1,16 @@ +# DeploymentDeployment + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**UserEmail** | **string** | Identity of the user that triggered this deployment. | [optional] [default to null] +**DeployTime** | [**time.Time**](time.Time.md) | Required. Beginning of the lifetime of this deployment. | [optional] [default to null] +**UndeployTime** | [**time.Time**](time.Time.md) | End of the lifetime of this deployment. | [optional] [default to null] +**Config** | **string** | Configuration used to create this deployment. | [optional] [default to null] +**Address** | **string** | Address of the runtime element hosting this deployment. | [optional] [default to null] +**ResourceUri** | **[]string** | Output only. Resource URI for the artifact being deployed taken from the deployable field with the same name. | [optional] [default to null] +**Platform** | [***DeploymentPlatform**](DeploymentPlatform.md) | Platform hosting this deployment. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/DeploymentPlatform.md b/image-scanner/grafeas/docs/DeploymentPlatform.md new file mode 100644 index 000000000..fd5e05566 --- /dev/null +++ b/image-scanner/grafeas/docs/DeploymentPlatform.md @@ -0,0 +1,9 @@ +# DeploymentPlatform + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/DiscoveredAnalysisStatus.md b/image-scanner/grafeas/docs/DiscoveredAnalysisStatus.md new file mode 100644 index 000000000..5cf66c8c0 --- /dev/null +++ b/image-scanner/grafeas/docs/DiscoveredAnalysisStatus.md @@ -0,0 +1,9 @@ +# DiscoveredAnalysisStatus + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/DiscoveredContinuousAnalysis.md b/image-scanner/grafeas/docs/DiscoveredContinuousAnalysis.md new file mode 100644 index 000000000..61e65fc2c --- /dev/null +++ b/image-scanner/grafeas/docs/DiscoveredContinuousAnalysis.md @@ -0,0 +1,9 @@ +# DiscoveredContinuousAnalysis + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/DiscoveryDiscovered.md b/image-scanner/grafeas/docs/DiscoveryDiscovered.md new file mode 100644 index 000000000..b15ab2f35 --- /dev/null +++ b/image-scanner/grafeas/docs/DiscoveryDiscovered.md @@ -0,0 +1,13 @@ +# DiscoveryDiscovered + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ContinuousAnalysis** | [***DiscoveredContinuousAnalysis**](DiscoveredContinuousAnalysis.md) | Whether the resource is continuously analyzed. | [optional] [default to null] +**LastAnalysisTime** | [**time.Time**](time.Time.md) | The last time continuous analysis was done for this resource. Deprecated, do not use. | [optional] [default to null] +**AnalysisStatus** | [***DiscoveredAnalysisStatus**](DiscoveredAnalysisStatus.md) | The status of discovery for the resource. | [optional] [default to null] +**AnalysisStatusError** | [***RpcStatus**](rpcStatus.md) | When an error is encountered this will contain a LocalizedMessage under details to show to the user. The LocalizedMessage is output only and populated by the API. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/DiscoveryDiscovery.md b/image-scanner/grafeas/docs/DiscoveryDiscovery.md new file mode 100644 index 000000000..87047b46a --- /dev/null +++ b/image-scanner/grafeas/docs/DiscoveryDiscovery.md @@ -0,0 +1,10 @@ +# DiscoveryDiscovery + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**AnalysisKind** | [***V1beta1NoteKind**](v1beta1NoteKind.md) | Required. Immutable. The kind of analysis that is handled by this discovery. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/GrafeasV1Beta1Api.md b/image-scanner/grafeas/docs/GrafeasV1Beta1Api.md new file mode 100644 index 000000000..b9bc0bbba --- /dev/null +++ b/image-scanner/grafeas/docs/GrafeasV1Beta1Api.md @@ -0,0 +1,461 @@ +# \GrafeasV1Beta1Api + +All URIs are relative to *http://localhost* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**BatchCreateNotes**](GrafeasV1Beta1Api.md#BatchCreateNotes) | **Post** /v1beta1/{parent=projects/*}/notes:batchCreate | Creates new notes in batch. +[**BatchCreateOccurrences**](GrafeasV1Beta1Api.md#BatchCreateOccurrences) | **Post** /v1beta1/{parent=projects/*}/occurrences:batchCreate | Creates new occurrences in batch. +[**CreateNote**](GrafeasV1Beta1Api.md#CreateNote) | **Post** /v1beta1/{parent=projects/*}/notes | Creates a new note. +[**CreateOccurrence**](GrafeasV1Beta1Api.md#CreateOccurrence) | **Post** /v1beta1/{parent=projects/*}/occurrences | Creates a new occurrence. +[**DeleteNote**](GrafeasV1Beta1Api.md#DeleteNote) | **Delete** /v1beta1/{name=projects/*/notes/*} | Deletes the specified note. +[**DeleteOccurrence**](GrafeasV1Beta1Api.md#DeleteOccurrence) | **Delete** /v1beta1/{name=projects/*/occurrences/*} | Deletes the specified occurrence. For example, use this method to delete an occurrence when the occurrence is no longer applicable for the given resource. +[**GetNote**](GrafeasV1Beta1Api.md#GetNote) | **Get** /v1beta1/{name=projects/*/notes/*} | Gets the specified note. +[**GetOccurrence**](GrafeasV1Beta1Api.md#GetOccurrence) | **Get** /v1beta1/{name=projects/*/occurrences/*} | Gets the specified occurrence. +[**GetOccurrenceNote**](GrafeasV1Beta1Api.md#GetOccurrenceNote) | **Get** /v1beta1/{name=projects/*/occurrences/*}/notes | Gets the note attached to the specified occurrence. Consumer projects can use this method to get a note that belongs to a provider project. +[**GetVulnerabilityOccurrencesSummary**](GrafeasV1Beta1Api.md#GetVulnerabilityOccurrencesSummary) | **Get** /v1beta1/{parent=projects/*}/occurrences:vulnerabilitySummary | Gets a summary of the number and severity of occurrences. +[**ListNoteOccurrences**](GrafeasV1Beta1Api.md#ListNoteOccurrences) | **Get** /v1beta1/{name=projects/*/notes/*}/occurrences | Lists occurrences referencing the specified note. Provider projects can use this method to get all occurrences across consumer projects referencing the specified note. +[**ListNotes**](GrafeasV1Beta1Api.md#ListNotes) | **Get** /v1beta1/{parent=projects/*}/notes | Lists notes for the specified project. +[**ListOccurrences**](GrafeasV1Beta1Api.md#ListOccurrences) | **Get** /v1beta1/{parent=projects/*}/occurrences | Lists occurrences for the specified project. +[**UpdateNote**](GrafeasV1Beta1Api.md#UpdateNote) | **Patch** /v1beta1/{name=projects/*/notes/*} | Updates the specified note. +[**UpdateOccurrence**](GrafeasV1Beta1Api.md#UpdateOccurrence) | **Patch** /v1beta1/{name=projects/*/occurrences/*} | Updates the specified occurrence. + + +# **BatchCreateNotes** +> V1beta1BatchCreateNotesResponse BatchCreateNotes(ctx, parent, body) +Creates new notes in batch. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **parent** | **string**| The name of the project in the form of `projects/[PROJECT_ID]`, under which the notes are to be created. | + **body** | [**V1beta1BatchCreateNotesRequest**](V1beta1BatchCreateNotesRequest.md)| | + +### Return type + +[**V1beta1BatchCreateNotesResponse**](v1beta1BatchCreateNotesResponse.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **BatchCreateOccurrences** +> V1beta1BatchCreateOccurrencesResponse BatchCreateOccurrences(ctx, parent, body) +Creates new occurrences in batch. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **parent** | **string**| The name of the project in the form of `projects/[PROJECT_ID]`, under which the occurrences are to be created. | + **body** | [**V1beta1BatchCreateOccurrencesRequest**](V1beta1BatchCreateOccurrencesRequest.md)| | + +### Return type + +[**V1beta1BatchCreateOccurrencesResponse**](v1beta1BatchCreateOccurrencesResponse.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **CreateNote** +> V1beta1Note CreateNote(ctx, parent, body) +Creates a new note. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **parent** | **string**| The name of the project in the form of `projects/[PROJECT_ID]`, under which the note is to be created. | + **body** | [**V1beta1Note**](V1beta1Note.md)| The note to create. | + +### Return type + +[**V1beta1Note**](v1beta1Note.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **CreateOccurrence** +> V1beta1Occurrence CreateOccurrence(ctx, parent, body) +Creates a new occurrence. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **parent** | **string**| The name of the project in the form of `projects/[PROJECT_ID]`, under which the occurrence is to be created. | + **body** | [**V1beta1Occurrence**](V1beta1Occurrence.md)| The occurrence to create. | + +### Return type + +[**V1beta1Occurrence**](v1beta1Occurrence.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **DeleteNote** +> interface{} DeleteNote(ctx, name) +Deletes the specified note. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **name** | **string**| The name of the note in the form of `projects/[PROVIDER_ID]/notes/[NOTE_ID]`. | + +### Return type + +[**interface{}**](interface{}.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **DeleteOccurrence** +> interface{} DeleteOccurrence(ctx, name) +Deletes the specified occurrence. For example, use this method to delete an occurrence when the occurrence is no longer applicable for the given resource. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **name** | **string**| The name of the occurrence in the form of `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`. | + +### Return type + +[**interface{}**](interface{}.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **GetNote** +> V1beta1Note GetNote(ctx, name) +Gets the specified note. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **name** | **string**| The name of the note in the form of `projects/[PROVIDER_ID]/notes/[NOTE_ID]`. | + +### Return type + +[**V1beta1Note**](v1beta1Note.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **GetOccurrence** +> V1beta1Occurrence GetOccurrence(ctx, name) +Gets the specified occurrence. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **name** | **string**| The name of the occurrence in the form of `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`. | + +### Return type + +[**V1beta1Occurrence**](v1beta1Occurrence.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **GetOccurrenceNote** +> V1beta1Note GetOccurrenceNote(ctx, name) +Gets the note attached to the specified occurrence. Consumer projects can use this method to get a note that belongs to a provider project. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **name** | **string**| The name of the occurrence in the form of `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`. | + +### Return type + +[**V1beta1Note**](v1beta1Note.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **GetVulnerabilityOccurrencesSummary** +> V1beta1VulnerabilityOccurrencesSummary GetVulnerabilityOccurrencesSummary(ctx, parent, optional) +Gets a summary of the number and severity of occurrences. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **parent** | **string**| The name of the project to get a vulnerability summary for in the form of `projects/[PROJECT_ID]`. | + **optional** | ***GetVulnerabilityOccurrencesSummaryOpts** | optional parameters | nil if no parameters + +### Optional Parameters +Optional parameters are passed through a pointer to a GetVulnerabilityOccurrencesSummaryOpts struct + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + + **filter** | **optional.String**| The filter expression. | + +### Return type + +[**V1beta1VulnerabilityOccurrencesSummary**](v1beta1VulnerabilityOccurrencesSummary.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ListNoteOccurrences** +> V1beta1ListNoteOccurrencesResponse ListNoteOccurrences(ctx, name, optional) +Lists occurrences referencing the specified note. Provider projects can use this method to get all occurrences across consumer projects referencing the specified note. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **name** | **string**| The name of the note to list occurrences for in the form of `projects/[PROVIDER_ID]/notes/[NOTE_ID]`. | + **optional** | ***ListNoteOccurrencesOpts** | optional parameters | nil if no parameters + +### Optional Parameters +Optional parameters are passed through a pointer to a ListNoteOccurrencesOpts struct + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + + **filter** | **optional.String**| The filter expression. | + **pageSize** | **optional.Int32**| Number of occurrences to return in the list. | + **pageToken** | **optional.String**| Token to provide to skip to a particular spot in the list. | + +### Return type + +[**V1beta1ListNoteOccurrencesResponse**](v1beta1ListNoteOccurrencesResponse.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ListNotes** +> V1beta1ListNotesResponse ListNotes(ctx, parent, optional) +Lists notes for the specified project. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **parent** | **string**| The name of the project to list notes for in the form of `projects/[PROJECT_ID]`. | + **optional** | ***ListNotesOpts** | optional parameters | nil if no parameters + +### Optional Parameters +Optional parameters are passed through a pointer to a ListNotesOpts struct + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + + **filter** | **optional.String**| The filter expression. | + **pageSize** | **optional.Int32**| Number of notes to return in the list. Must be positive. Max allowed page size is 1000. If not specified, page size defaults to 20. | + **pageToken** | **optional.String**| Token to provide to skip to a particular spot in the list. | + +### Return type + +[**V1beta1ListNotesResponse**](v1beta1ListNotesResponse.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ListOccurrences** +> V1beta1ListOccurrencesResponse ListOccurrences(ctx, parent, optional) +Lists occurrences for the specified project. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **parent** | **string**| The name of the project to list occurrences for in the form of `projects/[PROJECT_ID]`. | + **optional** | ***ListOccurrencesOpts** | optional parameters | nil if no parameters + +### Optional Parameters +Optional parameters are passed through a pointer to a ListOccurrencesOpts struct + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + + **filter** | **optional.String**| The filter expression. | + **pageSize** | **optional.Int32**| Number of occurrences to return in the list. Must be positive. Max allowed page size is 1000. If not specified, page size defaults to 20. | + **pageToken** | **optional.String**| Token to provide to skip to a particular spot in the list. | + +### Return type + +[**V1beta1ListOccurrencesResponse**](v1beta1ListOccurrencesResponse.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **UpdateNote** +> V1beta1Note UpdateNote(ctx, name, body) +Updates the specified note. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **name** | **string**| The name of the note in the form of `projects/[PROVIDER_ID]/notes/[NOTE_ID]`. | + **body** | [**V1beta1Note**](V1beta1Note.md)| The updated note. | + +### Return type + +[**V1beta1Note**](v1beta1Note.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **UpdateOccurrence** +> V1beta1Occurrence UpdateOccurrence(ctx, name, body) +Updates the specified occurrence. + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc. + **name** | **string**| The name of the occurrence in the form of `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`. | + **body** | [**V1beta1Occurrence**](V1beta1Occurrence.md)| The updated occurrence. | + +### Return type + +[**V1beta1Occurrence**](v1beta1Occurrence.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/image-scanner/grafeas/docs/Grafeasv1beta1Signature.md b/image-scanner/grafeas/docs/Grafeasv1beta1Signature.md new file mode 100644 index 000000000..6329fdc52 --- /dev/null +++ b/image-scanner/grafeas/docs/Grafeasv1beta1Signature.md @@ -0,0 +1,11 @@ +# Grafeasv1beta1Signature + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Signature** | **string** | The content of the signature, an opaque bytestring. The payload that this signature verifies MUST be unambiguously provided with the Signature during verification. A wrapper message might provide the payload explicitly. Alternatively, a message might have a canonical serialization that can always be unambiguously computed to derive the payload. | [optional] [default to null] +**PublicKeyId** | **string** | The identifier for the public key that verifies this signature. * The `public_key_id` is required. * The `public_key_id` SHOULD be an RFC3986 conformant URI. * When possible, the `public_key_id` SHOULD be an immutable reference, such as a cryptographic digest. Examples of valid `public_key_id`s: OpenPGP V4 public key fingerprint: * \"openpgp4fpr:74FAF3B861BDA0870C7B6DEF607E48D2A663AEEA\" See https://www.iana.org/assignments/uri-schemes/prov/openpgp4fpr for more details on this scheme. RFC6920 digest-named SubjectPublicKeyInfo (digest of the DER serialization): * \"ni:///sha-256;cD9o9Cq6LG3jD0iKXqEi_vdjJGecm_iXkbqVoScViaU\" * \"nih:///sha-256;703f68f42aba2c6de30f488a5ea122fef76324679c9bf89791ba95a1271589a5\" | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/HashHashType.md b/image-scanner/grafeas/docs/HashHashType.md new file mode 100644 index 000000000..5c1fdd816 --- /dev/null +++ b/image-scanner/grafeas/docs/HashHashType.md @@ -0,0 +1,9 @@ +# HashHashType + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/ImageBasis.md b/image-scanner/grafeas/docs/ImageBasis.md new file mode 100644 index 000000000..9b48cb900 --- /dev/null +++ b/image-scanner/grafeas/docs/ImageBasis.md @@ -0,0 +1,11 @@ +# ImageBasis + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ResourceUrl** | **string** | Required. Immutable. The resource_url for the resource representing the basis of associated occurrence images. | [optional] [default to null] +**Fingerprint** | [***ImageFingerprint**](imageFingerprint.md) | Required. Immutable. The fingerprint of the base image. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/ImageDerived.md b/image-scanner/grafeas/docs/ImageDerived.md new file mode 100644 index 000000000..4e5380941 --- /dev/null +++ b/image-scanner/grafeas/docs/ImageDerived.md @@ -0,0 +1,13 @@ +# ImageDerived + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Fingerprint** | [***ImageFingerprint**](imageFingerprint.md) | Required. The fingerprint of the derived image. | [optional] [default to null] +**Distance** | **int32** | Output only. The number of layers by which this image differs from the associated image basis. | [optional] [default to null] +**LayerInfo** | [**[]ImageLayer**](imageLayer.md) | This contains layer-specific metadata, if populated it has length \"distance\" and is ordered with [distance] being the layer immediately following the base image and [1] being the final layer. | [optional] [default to null] +**BaseResourceUrl** | **string** | Output only. This contains the base image URL for the derived image occurrence. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/ImageFingerprint.md b/image-scanner/grafeas/docs/ImageFingerprint.md new file mode 100644 index 000000000..6500afd75 --- /dev/null +++ b/image-scanner/grafeas/docs/ImageFingerprint.md @@ -0,0 +1,12 @@ +# ImageFingerprint + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**V1Name** | **string** | Required. The layer ID of the final layer in the Docker image's v1 representation. | [optional] [default to null] +**V2Blob** | **[]string** | Required. The ordered list of v2 blobs that represent a given image. | [optional] [default to null] +**V2Name** | **string** | Output only. The name of the image's v2 blobs computed via: [bottom] := v2_blob[bottom] [N] := sha256(v2_blob[N] + \" \" + v2_name[N+1]) Only the name of the final blob is kept. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/ImageLayer.md b/image-scanner/grafeas/docs/ImageLayer.md new file mode 100644 index 000000000..28fc2da26 --- /dev/null +++ b/image-scanner/grafeas/docs/ImageLayer.md @@ -0,0 +1,11 @@ +# ImageLayer + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Directive** | [***LayerDirective**](LayerDirective.md) | Required. The recovered Dockerfile directive used to construct this layer. | [optional] [default to null] +**Arguments** | **string** | The recovered arguments to the Dockerfile directive. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/InTotoArtifactRule.md b/image-scanner/grafeas/docs/InTotoArtifactRule.md new file mode 100644 index 000000000..f10e5192d --- /dev/null +++ b/image-scanner/grafeas/docs/InTotoArtifactRule.md @@ -0,0 +1,10 @@ +# InTotoArtifactRule + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ArtifactRule** | **[]string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/IntotoInToto.md b/image-scanner/grafeas/docs/IntotoInToto.md new file mode 100644 index 000000000..09d2d3366 --- /dev/null +++ b/image-scanner/grafeas/docs/IntotoInToto.md @@ -0,0 +1,15 @@ +# IntotoInToto + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**StepName** | **string** | This field identifies the name of the step in the supply chain. | [optional] [default to null] +**SigningKeys** | [**[]IntotoSigningKey**](intotoSigningKey.md) | This field contains the public keys that can be used to verify the signatures on the step metadata. | [optional] [default to null] +**ExpectedMaterials** | [**[]InTotoArtifactRule**](InTotoArtifactRule.md) | The following fields contain in-toto artifact rules identifying the artifacts that enter this supply chain step, and exit the supply chain step, i.e. materials and products of the step. | [optional] [default to null] +**ExpectedProducts** | [**[]InTotoArtifactRule**](InTotoArtifactRule.md) | | [optional] [default to null] +**ExpectedCommand** | **[]string** | This field contains the expected command used to perform the step. | [optional] [default to null] +**Threshold** | **string** | This field contains a value that indicates the minimum number of keys that need to be used to sign the step's in-toto link. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/IntotoLink.md b/image-scanner/grafeas/docs/IntotoLink.md new file mode 100644 index 000000000..f2fa1dcc6 --- /dev/null +++ b/image-scanner/grafeas/docs/IntotoLink.md @@ -0,0 +1,14 @@ +# IntotoLink + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**EffectiveCommand** | **[]string** | | [optional] [default to null] +**Materials** | [**[]IntotoLinkArtifact**](intotoLinkArtifact.md) | | [optional] [default to null] +**Products** | [**[]IntotoLinkArtifact**](intotoLinkArtifact.md) | Products are the supply chain artifacts generated as a result of the step. The structure is identical to that of materials. | [optional] [default to null] +**Byproducts** | [***LinkByProducts**](LinkByProducts.md) | ByProducts are data generated as part of a software supply chain step, but are not the actual result of the step. | [optional] [default to null] +**Environment** | [***LinkEnvironment**](LinkEnvironment.md) | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/IntotoLinkArtifact.md b/image-scanner/grafeas/docs/IntotoLinkArtifact.md new file mode 100644 index 000000000..688d3ace9 --- /dev/null +++ b/image-scanner/grafeas/docs/IntotoLinkArtifact.md @@ -0,0 +1,11 @@ +# IntotoLinkArtifact + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ResourceUri** | **string** | | [optional] [default to null] +**Hashes** | [***LinkArtifactHashes**](LinkArtifactHashes.md) | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/IntotoSigningKey.md b/image-scanner/grafeas/docs/IntotoSigningKey.md new file mode 100644 index 000000000..a25b71f6e --- /dev/null +++ b/image-scanner/grafeas/docs/IntotoSigningKey.md @@ -0,0 +1,13 @@ +# IntotoSigningKey + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**KeyId** | **string** | key_id is an identifier for the signing key. | [optional] [default to null] +**KeyType** | **string** | This field identifies the specific signing method. Eg: \"rsa\", \"ed25519\", and \"ecdsa\". | [optional] [default to null] +**PublicKeyValue** | **string** | This field contains the actual public key. | [optional] [default to null] +**KeyScheme** | **string** | This field contains the corresponding signature scheme. Eg: \"rsassa-pss-sha256\". | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/LayerDirective.md b/image-scanner/grafeas/docs/LayerDirective.md new file mode 100644 index 000000000..445dd9125 --- /dev/null +++ b/image-scanner/grafeas/docs/LayerDirective.md @@ -0,0 +1,9 @@ +# LayerDirective + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/LinkArtifactHashes.md b/image-scanner/grafeas/docs/LinkArtifactHashes.md new file mode 100644 index 000000000..d6042f532 --- /dev/null +++ b/image-scanner/grafeas/docs/LinkArtifactHashes.md @@ -0,0 +1,10 @@ +# LinkArtifactHashes + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Sha256** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/LinkByProducts.md b/image-scanner/grafeas/docs/LinkByProducts.md new file mode 100644 index 000000000..ddf72c799 --- /dev/null +++ b/image-scanner/grafeas/docs/LinkByProducts.md @@ -0,0 +1,10 @@ +# LinkByProducts + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**CustomValues** | **map[string]string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/LinkEnvironment.md b/image-scanner/grafeas/docs/LinkEnvironment.md new file mode 100644 index 000000000..464715703 --- /dev/null +++ b/image-scanner/grafeas/docs/LinkEnvironment.md @@ -0,0 +1,10 @@ +# LinkEnvironment + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**CustomValues** | **map[string]string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/PackageArchitecture.md b/image-scanner/grafeas/docs/PackageArchitecture.md new file mode 100644 index 000000000..903eb48ca --- /dev/null +++ b/image-scanner/grafeas/docs/PackageArchitecture.md @@ -0,0 +1,9 @@ +# PackageArchitecture + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/PackageDistribution.md b/image-scanner/grafeas/docs/PackageDistribution.md new file mode 100644 index 000000000..c15a9fc89 --- /dev/null +++ b/image-scanner/grafeas/docs/PackageDistribution.md @@ -0,0 +1,15 @@ +# PackageDistribution + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**CpeUri** | **string** | Required. The cpe_uri in [CPE format](https://cpe.mitre.org/specification/) denoting the package manager version distributing a package. | [optional] [default to null] +**Architecture** | [***PackageArchitecture**](packageArchitecture.md) | The CPU architecture for which packages in this distribution channel were built. | [optional] [default to null] +**LatestVersion** | [***PackageVersion**](packageVersion.md) | The latest available version of this package in this distribution channel. | [optional] [default to null] +**Maintainer** | **string** | A freeform string denoting the maintainer of this package. | [optional] [default to null] +**Url** | **string** | The distribution channel-specific homepage for this package. | [optional] [default to null] +**Description** | **string** | The distribution channel-specific description of this package. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/PackageInstallation.md b/image-scanner/grafeas/docs/PackageInstallation.md new file mode 100644 index 000000000..0589f789a --- /dev/null +++ b/image-scanner/grafeas/docs/PackageInstallation.md @@ -0,0 +1,11 @@ +# PackageInstallation + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Name** | **string** | Output only. The name of the installed package. | [optional] [default to null] +**Location** | [**[]V1beta1packageLocation**](v1beta1packageLocation.md) | Required. All of the places within the filesystem versions of this package have been found. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/PackagePackage.md b/image-scanner/grafeas/docs/PackagePackage.md new file mode 100644 index 000000000..c250883c4 --- /dev/null +++ b/image-scanner/grafeas/docs/PackagePackage.md @@ -0,0 +1,11 @@ +# PackagePackage + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Name** | **string** | Required. Immutable. The name of the package. | [optional] [default to null] +**Distribution** | [**[]PackageDistribution**](packageDistribution.md) | The various channels by which a package is distributed. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/PackageVersion.md b/image-scanner/grafeas/docs/PackageVersion.md new file mode 100644 index 000000000..457477cb9 --- /dev/null +++ b/image-scanner/grafeas/docs/PackageVersion.md @@ -0,0 +1,13 @@ +# PackageVersion + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Epoch** | **int32** | Used to correct mistakes in the version numbering scheme. | [optional] [default to null] +**Name** | **string** | Required only when version kind is NORMAL. The main part of the version name. | [optional] [default to null] +**Revision** | **string** | The iteration of the package build from the above version. | [optional] [default to null] +**Kind** | [***VersionVersionKind**](VersionVersionKind.md) | Required. Distinguishes between sentinel MIN/MAX versions and normal versions. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/ProtobufAny.md b/image-scanner/grafeas/docs/ProtobufAny.md new file mode 100644 index 000000000..50aaf9e77 --- /dev/null +++ b/image-scanner/grafeas/docs/ProtobufAny.md @@ -0,0 +1,11 @@ +# ProtobufAny + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**TypeUrl** | **string** | A URL/resource name that uniquely identifies the type of the serialized protocol buffer message. This string must contain at least one \"/\" character. The last segment of the URL's path must represent the fully qualified name of the type (as in `path/google.protobuf.Duration`). The name should be in a canonical form (e.g., leading \".\" is not accepted). In practice, teams usually precompile into the binary all types that they expect it to use in the context of Any. However, for URLs which use the scheme `http`, `https`, or no scheme, one can optionally set up a type server that maps type URLs to message definitions as follows: * If no scheme is provided, `https` is assumed. * An HTTP GET on the URL must yield a [google.protobuf.Type][] value in binary format, or produce an error. * Applications are allowed to cache lookup results based on the URL, or have them precompiled into a binary to avoid any lookup. Therefore, binary compatibility needs to be preserved on changes to types. (Use versioned type names to manage breaking changes.) Note: this functionality is not currently available in the official protobuf release, and it is not used for type URLs beginning with type.googleapis.com. Schemes other than `http`, `https` (or the empty scheme) might be used with implementation specific semantics. | [optional] [default to null] +**Value** | **string** | Must be a valid serialized protocol buffer of the above specified type. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/ProtobufFieldMask.md b/image-scanner/grafeas/docs/ProtobufFieldMask.md new file mode 100644 index 000000000..abf911d8a --- /dev/null +++ b/image-scanner/grafeas/docs/ProtobufFieldMask.md @@ -0,0 +1,10 @@ +# ProtobufFieldMask + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Paths** | **[]string** | The set of field mask paths. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/ProvenanceBuildProvenance.md b/image-scanner/grafeas/docs/ProvenanceBuildProvenance.md new file mode 100644 index 000000000..1fe7a0924 --- /dev/null +++ b/image-scanner/grafeas/docs/ProvenanceBuildProvenance.md @@ -0,0 +1,22 @@ +# ProvenanceBuildProvenance + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Id** | **string** | Required. Unique identifier of the build. | [optional] [default to null] +**ProjectId** | **string** | ID of the project. | [optional] [default to null] +**Commands** | [**[]ProvenanceCommand**](provenanceCommand.md) | Commands requested by the build. | [optional] [default to null] +**BuiltArtifacts** | [**[]V1beta1provenanceArtifact**](v1beta1provenanceArtifact.md) | Output of the build. | [optional] [default to null] +**CreateTime** | [**time.Time**](time.Time.md) | Time at which the build was created. | [optional] [default to null] +**StartTime** | [**time.Time**](time.Time.md) | Time at which execution of the build was started. | [optional] [default to null] +**EndTime** | [**time.Time**](time.Time.md) | Time at which execution of the build was finished. | [optional] [default to null] +**Creator** | **string** | E-mail address of the user who initiated this build. Note that this was the user's e-mail address at the time the build was initiated; this address may not represent the same end-user for all time. | [optional] [default to null] +**LogsUri** | **string** | URI where any logs for this provenance were written. | [optional] [default to null] +**SourceProvenance** | [***ProvenanceSource**](provenanceSource.md) | Details of the Source input to the build. | [optional] [default to null] +**TriggerId** | **string** | Trigger identifier if the build was triggered automatically; empty if not. | [optional] [default to null] +**BuildOptions** | **map[string]string** | Special options applied to this build. This is a catch-all field where build providers can enter any desired additional details. | [optional] [default to null] +**BuilderVersion** | **string** | Version string of the builder at the time this build was executed. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/ProvenanceCommand.md b/image-scanner/grafeas/docs/ProvenanceCommand.md new file mode 100644 index 000000000..45507b632 --- /dev/null +++ b/image-scanner/grafeas/docs/ProvenanceCommand.md @@ -0,0 +1,15 @@ +# ProvenanceCommand + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Name** | **string** | Required. Name of the command, as presented on the command line, or if the command is packaged as a Docker container, as presented to `docker pull`. | [optional] [default to null] +**Env** | **[]string** | Environment variables set before running this command. | [optional] [default to null] +**Args** | **[]string** | Command-line arguments used when executing this command. | [optional] [default to null] +**Dir** | **string** | Working directory (relative to project source root) used when running this command. | [optional] [default to null] +**Id** | **string** | Optional unique identifier for this command, used in wait_for to reference this command as a dependency. | [optional] [default to null] +**WaitFor** | **[]string** | The ID(s) of the command(s) that this command depends on. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/ProvenanceFileHashes.md b/image-scanner/grafeas/docs/ProvenanceFileHashes.md new file mode 100644 index 000000000..266e581c0 --- /dev/null +++ b/image-scanner/grafeas/docs/ProvenanceFileHashes.md @@ -0,0 +1,10 @@ +# ProvenanceFileHashes + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**FileHash** | [**[]ProvenanceHash**](provenanceHash.md) | Required. Collection of file hashes. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/ProvenanceHash.md b/image-scanner/grafeas/docs/ProvenanceHash.md new file mode 100644 index 000000000..fe9aedef1 --- /dev/null +++ b/image-scanner/grafeas/docs/ProvenanceHash.md @@ -0,0 +1,11 @@ +# ProvenanceHash + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Type_** | [***HashHashType**](HashHashType.md) | Required. The type of hash that was performed. | [optional] [default to null] +**Value** | **string** | Required. The hash value. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/ProvenanceSource.md b/image-scanner/grafeas/docs/ProvenanceSource.md new file mode 100644 index 000000000..ce4cbbe79 --- /dev/null +++ b/image-scanner/grafeas/docs/ProvenanceSource.md @@ -0,0 +1,13 @@ +# ProvenanceSource + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ArtifactStorageSourceUri** | **string** | If provided, the input binary artifacts for the build came from this location. | [optional] [default to null] +**FileHashes** | [**map[string]ProvenanceFileHashes**](provenanceFileHashes.md) | Hash(es) of the build source, which can be used to verify that the original source integrity was maintained in the build. The keys to this map are file paths used as build source and the values contain the hash values for those files. If the build source came in a single package such as a gzipped tarfile (.tar.gz), the FileHash will be for the single path to that file. | [optional] [default to null] +**Context** | [***SourceSourceContext**](sourceSourceContext.md) | If provided, the source code used for the build came from this location. | [optional] [default to null] +**AdditionalContexts** | [**[]SourceSourceContext**](sourceSourceContext.md) | If provided, some of the source code used for the build may be found in these locations, in the case where the source repository had multiple remotes or submodules. This list will not include the context specified in the context field. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/RpcStatus.md b/image-scanner/grafeas/docs/RpcStatus.md new file mode 100644 index 000000000..52a18490d --- /dev/null +++ b/image-scanner/grafeas/docs/RpcStatus.md @@ -0,0 +1,12 @@ +# RpcStatus + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Code** | **int32** | The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. | [optional] [default to null] +**Message** | **string** | A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. | [optional] [default to null] +**Details** | [**[]ProtobufAny**](protobufAny.md) | A list of messages that carry the error details. There is a common set of message types for APIs to use. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/SourceAliasContext.md b/image-scanner/grafeas/docs/SourceAliasContext.md new file mode 100644 index 000000000..988ee0c55 --- /dev/null +++ b/image-scanner/grafeas/docs/SourceAliasContext.md @@ -0,0 +1,11 @@ +# SourceAliasContext + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Kind** | [***AliasContextKind**](AliasContextKind.md) | The alias kind. | [optional] [default to null] +**Name** | **string** | The alias name. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/SourceCloudRepoSourceContext.md b/image-scanner/grafeas/docs/SourceCloudRepoSourceContext.md new file mode 100644 index 000000000..8d99968f6 --- /dev/null +++ b/image-scanner/grafeas/docs/SourceCloudRepoSourceContext.md @@ -0,0 +1,12 @@ +# SourceCloudRepoSourceContext + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**RepoId** | [***SourceRepoId**](sourceRepoId.md) | The ID of the repo. | [optional] [default to null] +**RevisionId** | **string** | A revision ID. | [optional] [default to null] +**AliasContext** | [***SourceAliasContext**](sourceAliasContext.md) | An alias, which may be a branch or tag. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/SourceGerritSourceContext.md b/image-scanner/grafeas/docs/SourceGerritSourceContext.md new file mode 100644 index 000000000..cfd0d745c --- /dev/null +++ b/image-scanner/grafeas/docs/SourceGerritSourceContext.md @@ -0,0 +1,13 @@ +# SourceGerritSourceContext + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**HostUri** | **string** | The URI of a running Gerrit instance. | [optional] [default to null] +**GerritProject** | **string** | The full project name within the host. Projects may be nested, so \"project/subproject\" is a valid project name. The \"repo name\" is the hostURI/project. | [optional] [default to null] +**RevisionId** | **string** | A revision (commit) ID. | [optional] [default to null] +**AliasContext** | [***SourceAliasContext**](sourceAliasContext.md) | An alias, which may be a branch or tag. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/SourceGitSourceContext.md b/image-scanner/grafeas/docs/SourceGitSourceContext.md new file mode 100644 index 000000000..2ad3cf443 --- /dev/null +++ b/image-scanner/grafeas/docs/SourceGitSourceContext.md @@ -0,0 +1,11 @@ +# SourceGitSourceContext + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Url** | **string** | Git repository URL. | [optional] [default to null] +**RevisionId** | **string** | Git commit hash. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/SourceProjectRepoId.md b/image-scanner/grafeas/docs/SourceProjectRepoId.md new file mode 100644 index 000000000..ad514c171 --- /dev/null +++ b/image-scanner/grafeas/docs/SourceProjectRepoId.md @@ -0,0 +1,11 @@ +# SourceProjectRepoId + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ProjectId** | **string** | The ID of the project. | [optional] [default to null] +**RepoName** | **string** | The name of the repo. Leave empty for the default repo. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/SourceRepoId.md b/image-scanner/grafeas/docs/SourceRepoId.md new file mode 100644 index 000000000..cf9a0c9a2 --- /dev/null +++ b/image-scanner/grafeas/docs/SourceRepoId.md @@ -0,0 +1,11 @@ +# SourceRepoId + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ProjectRepoId** | [***SourceProjectRepoId**](sourceProjectRepoId.md) | A combination of a project ID and a repo name. | [optional] [default to null] +**Uid** | **string** | A server-assigned, globally unique identifier. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/SourceSourceContext.md b/image-scanner/grafeas/docs/SourceSourceContext.md new file mode 100644 index 000000000..2f33d3d6c --- /dev/null +++ b/image-scanner/grafeas/docs/SourceSourceContext.md @@ -0,0 +1,13 @@ +# SourceSourceContext + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**CloudRepo** | [***SourceCloudRepoSourceContext**](sourceCloudRepoSourceContext.md) | A SourceContext referring to a revision in a Google Cloud Source Repo. | [optional] [default to null] +**Gerrit** | [***SourceGerritSourceContext**](sourceGerritSourceContext.md) | A SourceContext referring to a Gerrit project. | [optional] [default to null] +**Git** | [***SourceGitSourceContext**](sourceGitSourceContext.md) | A SourceContext referring to any third party Git repo (e.g., GitHub). | [optional] [default to null] +**Labels** | **map[string]string** | Labels with user defined metadata. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1BatchCreateNotesRequest.md b/image-scanner/grafeas/docs/V1beta1BatchCreateNotesRequest.md new file mode 100644 index 000000000..e3360623c --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1BatchCreateNotesRequest.md @@ -0,0 +1,11 @@ +# V1beta1BatchCreateNotesRequest + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Parent** | **string** | The name of the project in the form of `projects/[PROJECT_ID]`, under which the notes are to be created. | [optional] [default to null] +**Notes** | [**map[string]V1beta1Note**](v1beta1Note.md) | The notes to create. Max allowed length is 1000. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1BatchCreateNotesResponse.md b/image-scanner/grafeas/docs/V1beta1BatchCreateNotesResponse.md new file mode 100644 index 000000000..64251e6e5 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1BatchCreateNotesResponse.md @@ -0,0 +1,10 @@ +# V1beta1BatchCreateNotesResponse + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Notes** | [**[]V1beta1Note**](v1beta1Note.md) | The notes that were created. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1BatchCreateOccurrencesRequest.md b/image-scanner/grafeas/docs/V1beta1BatchCreateOccurrencesRequest.md new file mode 100644 index 000000000..e772586ff --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1BatchCreateOccurrencesRequest.md @@ -0,0 +1,11 @@ +# V1beta1BatchCreateOccurrencesRequest + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Parent** | **string** | The name of the project in the form of `projects/[PROJECT_ID]`, under which the occurrences are to be created. | [optional] [default to null] +**Occurrences** | [**[]V1beta1Occurrence**](v1beta1Occurrence.md) | The occurrences to create. Max allowed length is 1000. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1BatchCreateOccurrencesResponse.md b/image-scanner/grafeas/docs/V1beta1BatchCreateOccurrencesResponse.md new file mode 100644 index 000000000..4c96b601b --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1BatchCreateOccurrencesResponse.md @@ -0,0 +1,10 @@ +# V1beta1BatchCreateOccurrencesResponse + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Occurrences** | [**[]V1beta1Occurrence**](v1beta1Occurrence.md) | The occurrences that were created. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1ListNoteOccurrencesResponse.md b/image-scanner/grafeas/docs/V1beta1ListNoteOccurrencesResponse.md new file mode 100644 index 000000000..92b9ca2f3 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1ListNoteOccurrencesResponse.md @@ -0,0 +1,11 @@ +# V1beta1ListNoteOccurrencesResponse + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Occurrences** | [**[]V1beta1Occurrence**](v1beta1Occurrence.md) | The occurrences attached to the specified note. | [optional] [default to null] +**NextPageToken** | **string** | Token to provide to skip to a particular spot in the list. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1ListNotesResponse.md b/image-scanner/grafeas/docs/V1beta1ListNotesResponse.md new file mode 100644 index 000000000..ed31ab944 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1ListNotesResponse.md @@ -0,0 +1,11 @@ +# V1beta1ListNotesResponse + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Notes** | [**[]V1beta1Note**](v1beta1Note.md) | The notes requested. | [optional] [default to null] +**NextPageToken** | **string** | The next pagination token in the list response. It should be used as `page_token` for the following request. An empty value means no more results. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1ListOccurrencesResponse.md b/image-scanner/grafeas/docs/V1beta1ListOccurrencesResponse.md new file mode 100644 index 000000000..08b4e9817 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1ListOccurrencesResponse.md @@ -0,0 +1,11 @@ +# V1beta1ListOccurrencesResponse + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Occurrences** | [**[]V1beta1Occurrence**](v1beta1Occurrence.md) | The occurrences requested. | [optional] [default to null] +**NextPageToken** | **string** | The next pagination token in the list response. It should be used as `page_token` for the following request. An empty value means no more results. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1Note.md b/image-scanner/grafeas/docs/V1beta1Note.md new file mode 100644 index 000000000..a62d4675e --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1Note.md @@ -0,0 +1,26 @@ +# V1beta1Note + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Name** | **string** | Output only. The name of the note in the form of `projects/[PROVIDER_ID]/notes/[NOTE_ID]`. | [optional] [default to null] +**ShortDescription** | **string** | A one sentence description of this note. | [optional] [default to null] +**LongDescription** | **string** | A detailed description of this note. | [optional] [default to null] +**Kind** | [***V1beta1NoteKind**](v1beta1NoteKind.md) | Output only. The type of analysis. This field can be used as a filter in list requests. | [optional] [default to null] +**RelatedUrl** | [**[]V1beta1RelatedUrl**](v1beta1RelatedUrl.md) | URLs associated with this note. | [optional] [default to null] +**ExpirationTime** | [**time.Time**](time.Time.md) | Time of expiration for this note. Empty if note does not expire. | [optional] [default to null] +**CreateTime** | [**time.Time**](time.Time.md) | Output only. The time this note was created. This field can be used as a filter in list requests. | [optional] [default to null] +**UpdateTime** | [**time.Time**](time.Time.md) | Output only. The time this note was last updated. This field can be used as a filter in list requests. | [optional] [default to null] +**RelatedNoteNames** | **[]string** | Other notes related to this note. | [optional] [default to null] +**Vulnerability** | [***VulnerabilityVulnerability**](vulnerabilityVulnerability.md) | A note describing a package vulnerability. | [optional] [default to null] +**Build** | [***BuildBuild**](buildBuild.md) | A note describing build provenance for a verifiable build. | [optional] [default to null] +**BaseImage** | [***ImageBasis**](imageBasis.md) | A note describing a base image. | [optional] [default to null] +**Package_** | [***PackagePackage**](packagePackage.md) | A note describing a package hosted by various package managers. | [optional] [default to null] +**Deployable** | [***DeploymentDeployable**](deploymentDeployable.md) | A note describing something that can be deployed. | [optional] [default to null] +**Discovery** | [***DiscoveryDiscovery**](discoveryDiscovery.md) | A note describing the initial analysis of a resource. | [optional] [default to null] +**AttestationAuthority** | [***AttestationAuthority**](attestationAuthority.md) | A note describing an attestation role. | [optional] [default to null] +**Intoto** | [***IntotoInToto**](intotoInToto.md) | A note describing an in-toto link. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1NoteKind.md b/image-scanner/grafeas/docs/V1beta1NoteKind.md new file mode 100644 index 000000000..35294be85 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1NoteKind.md @@ -0,0 +1,9 @@ +# V1beta1NoteKind + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1Occurrence.md b/image-scanner/grafeas/docs/V1beta1Occurrence.md new file mode 100644 index 000000000..78a979573 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1Occurrence.md @@ -0,0 +1,24 @@ +# V1beta1Occurrence + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Name** | **string** | Output only. The name of the occurrence in the form of `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`. | [optional] [default to null] +**Resource** | [***V1beta1Resource**](v1beta1Resource.md) | Required. Immutable. The resource for which the occurrence applies. | [optional] [default to null] +**NoteName** | **string** | Required. Immutable. The analysis note associated with this occurrence, in the form of `projects/[PROVIDER_ID]/notes/[NOTE_ID]`. This field can be used as a filter in list requests. | [optional] [default to null] +**Kind** | [***V1beta1NoteKind**](v1beta1NoteKind.md) | Output only. This explicitly denotes which of the occurrence details are specified. This field can be used as a filter in list requests. | [optional] [default to null] +**Remediation** | **string** | A description of actions that can be taken to remedy the note. | [optional] [default to null] +**CreateTime** | [**time.Time**](time.Time.md) | Output only. The time this occurrence was created. | [optional] [default to null] +**UpdateTime** | [**time.Time**](time.Time.md) | Output only. The time this occurrence was last updated. | [optional] [default to null] +**Vulnerability** | [***V1beta1vulnerabilityDetails**](v1beta1vulnerabilityDetails.md) | Describes a security vulnerability. | [optional] [default to null] +**Build** | [***V1beta1buildDetails**](v1beta1buildDetails.md) | Describes a verifiable build. | [optional] [default to null] +**DerivedImage** | [***V1beta1imageDetails**](v1beta1imageDetails.md) | Describes how this resource derives from the basis in the associated note. | [optional] [default to null] +**Installation** | [***V1beta1packageDetails**](v1beta1packageDetails.md) | Describes the installation of a package on the linked resource. | [optional] [default to null] +**Deployment** | [***V1beta1deploymentDetails**](v1beta1deploymentDetails.md) | Describes the deployment of an artifact on a runtime. | [optional] [default to null] +**Discovered** | [***V1beta1discoveryDetails**](v1beta1discoveryDetails.md) | Describes when a resource was discovered. | [optional] [default to null] +**Attestation** | [***V1beta1attestationDetails**](v1beta1attestationDetails.md) | Describes an attestation of an artifact. | [optional] [default to null] +**Intoto** | [***V1beta1intotoDetails**](v1beta1intotoDetails.md) | Describes a specific in-toto link. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1RelatedUrl.md b/image-scanner/grafeas/docs/V1beta1RelatedUrl.md new file mode 100644 index 000000000..4d7810500 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1RelatedUrl.md @@ -0,0 +1,11 @@ +# V1beta1RelatedUrl + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Url** | **string** | Specific URL associated with the resource. | [optional] [default to null] +**Label** | **string** | Label to describe usage of the URL. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1Resource.md b/image-scanner/grafeas/docs/V1beta1Resource.md new file mode 100644 index 000000000..f9add518e --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1Resource.md @@ -0,0 +1,12 @@ +# V1beta1Resource + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Name** | **string** | Deprecated, do not use. Use uri instead. The name of the resource. For example, the name of a Docker image - \"Debian\". | [optional] [default to null] +**Uri** | **string** | Required. The unique URI of the resource. For example, `https://gcr.io/project/image@sha256:foo` for a Docker image. | [optional] [default to null] +**ContentHash** | [***ProvenanceHash**](provenanceHash.md) | Deprecated, do not use. Use uri instead. The hash of the resource content. For example, the Docker digest. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1VulnerabilityOccurrencesSummary.md b/image-scanner/grafeas/docs/V1beta1VulnerabilityOccurrencesSummary.md new file mode 100644 index 000000000..b76bd366f --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1VulnerabilityOccurrencesSummary.md @@ -0,0 +1,10 @@ +# V1beta1VulnerabilityOccurrencesSummary + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Counts** | [**[]VulnerabilityOccurrencesSummaryFixableTotalByDigest**](VulnerabilityOccurrencesSummaryFixableTotalByDigest.md) | A listing by resource of the number of fixable and total vulnerabilities. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1attestationDetails.md b/image-scanner/grafeas/docs/V1beta1attestationDetails.md new file mode 100644 index 000000000..f41c0b669 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1attestationDetails.md @@ -0,0 +1,10 @@ +# V1beta1attestationDetails + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Attestation** | [***AttestationAttestation**](attestationAttestation.md) | Required. Attestation for the resource. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1buildDetails.md b/image-scanner/grafeas/docs/V1beta1buildDetails.md new file mode 100644 index 000000000..d8ea3575c --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1buildDetails.md @@ -0,0 +1,11 @@ +# V1beta1buildDetails + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Provenance** | [***ProvenanceBuildProvenance**](provenanceBuildProvenance.md) | Required. The actual provenance for the build. | [optional] [default to null] +**ProvenanceBytes** | **string** | Serialized JSON representation of the provenance, used in generating the build signature in the corresponding build note. After verifying the signature, `provenance_bytes` can be unmarshalled and compared to the provenance to confirm that it is unchanged. A base64-encoded string representation of the provenance bytes is used for the signature in order to interoperate with openssl which expects this format for signature verification. The serialized form is captured both to avoid ambiguity in how the provenance is marshalled to json as well to prevent incompatibilities with future changes. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1deploymentDetails.md b/image-scanner/grafeas/docs/V1beta1deploymentDetails.md new file mode 100644 index 000000000..f256d9f50 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1deploymentDetails.md @@ -0,0 +1,10 @@ +# V1beta1deploymentDetails + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Deployment** | [***DeploymentDeployment**](deploymentDeployment.md) | Required. Deployment history for the resource. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1discoveryDetails.md b/image-scanner/grafeas/docs/V1beta1discoveryDetails.md new file mode 100644 index 000000000..b1159b576 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1discoveryDetails.md @@ -0,0 +1,10 @@ +# V1beta1discoveryDetails + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Discovered** | [***DiscoveryDiscovered**](discoveryDiscovered.md) | Required. Analysis status for the discovered resource. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1imageDetails.md b/image-scanner/grafeas/docs/V1beta1imageDetails.md new file mode 100644 index 000000000..8f14be130 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1imageDetails.md @@ -0,0 +1,10 @@ +# V1beta1imageDetails + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**DerivedImage** | [***ImageDerived**](imageDerived.md) | Required. Immutable. The child image derived from the base image. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1intotoDetails.md b/image-scanner/grafeas/docs/V1beta1intotoDetails.md new file mode 100644 index 000000000..9910e253b --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1intotoDetails.md @@ -0,0 +1,11 @@ +# V1beta1intotoDetails + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Signatures** | [**[]V1beta1intotoSignature**](v1beta1intotoSignature.md) | | [optional] [default to null] +**Link** | [***IntotoLink**](intotoLink.md) | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1intotoSignature.md b/image-scanner/grafeas/docs/V1beta1intotoSignature.md new file mode 100644 index 000000000..7257016c1 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1intotoSignature.md @@ -0,0 +1,11 @@ +# V1beta1intotoSignature + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**KeyId** | **string** | | [optional] [default to null] +**Signature** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1packageDetails.md b/image-scanner/grafeas/docs/V1beta1packageDetails.md new file mode 100644 index 000000000..a840a2487 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1packageDetails.md @@ -0,0 +1,10 @@ +# V1beta1packageDetails + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Installation** | [***PackageInstallation**](packageInstallation.md) | Required. Where the package was installed. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1packageLocation.md b/image-scanner/grafeas/docs/V1beta1packageLocation.md new file mode 100644 index 000000000..20e8a1706 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1packageLocation.md @@ -0,0 +1,12 @@ +# V1beta1packageLocation + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**CpeUri** | **string** | Required. The CPE URI in [CPE format](https://cpe.mitre.org/specification/) denoting the package manager version distributing a package. | [optional] [default to null] +**Version** | [***PackageVersion**](packageVersion.md) | The version installed at this location. | [optional] [default to null] +**Path** | **string** | The path from which we gathered that this package/version is installed. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1provenanceArtifact.md b/image-scanner/grafeas/docs/V1beta1provenanceArtifact.md new file mode 100644 index 000000000..6584634b3 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1provenanceArtifact.md @@ -0,0 +1,12 @@ +# V1beta1provenanceArtifact + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Checksum** | **string** | Hash or checksum value of a binary, or Docker Registry 2.0 digest of a container. | [optional] [default to null] +**Id** | **string** | Artifact ID, if any; for container images, this will be a URL by digest like `gcr.io/projectID/imagename@sha256:123456`. | [optional] [default to null] +**Names** | **[]string** | Related artifact names. This may be the path to a binary or jar file, or in the case of a container build, the name used to push the container image to Google Container Registry, as presented to `docker push`. Note that a single Artifact ID can have multiple names, for example if two tags are applied to one image. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/V1beta1vulnerabilityDetails.md b/image-scanner/grafeas/docs/V1beta1vulnerabilityDetails.md new file mode 100644 index 000000000..10aeb25f0 --- /dev/null +++ b/image-scanner/grafeas/docs/V1beta1vulnerabilityDetails.md @@ -0,0 +1,17 @@ +# V1beta1vulnerabilityDetails + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Type_** | **string** | | [optional] [default to null] +**Severity** | [***VulnerabilitySeverity**](vulnerabilitySeverity.md) | Output only. The note provider assigned Severity of the vulnerability. | [optional] [default to null] +**CvssScore** | **float32** | Output only. The CVSS score of this vulnerability. CVSS score is on a scale of 0-10 where 0 indicates low severity and 10 indicates high severity. | [optional] [default to null] +**PackageIssue** | [**[]VulnerabilityPackageIssue**](vulnerabilityPackageIssue.md) | Required. The set of affected locations and their fixes (if available) within the associated resource. | [optional] [default to null] +**ShortDescription** | **string** | Output only. A one sentence description of this vulnerability. | [optional] [default to null] +**LongDescription** | **string** | Output only. A detailed description of this vulnerability. | [optional] [default to null] +**RelatedUrls** | [**[]V1beta1RelatedUrl**](v1beta1RelatedUrl.md) | Output only. URLs related to this vulnerability. | [optional] [default to null] +**EffectiveSeverity** | [***VulnerabilitySeverity**](vulnerabilitySeverity.md) | The distro assigned severity for this vulnerability when it is available, and note provider assigned severity when distro has not yet assigned a severity for this vulnerability. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/VersionVersionKind.md b/image-scanner/grafeas/docs/VersionVersionKind.md new file mode 100644 index 000000000..6af216999 --- /dev/null +++ b/image-scanner/grafeas/docs/VersionVersionKind.md @@ -0,0 +1,9 @@ +# VersionVersionKind + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/VulnerabilityCvsSv3.md b/image-scanner/grafeas/docs/VulnerabilityCvsSv3.md new file mode 100644 index 000000000..eaf329702 --- /dev/null +++ b/image-scanner/grafeas/docs/VulnerabilityCvsSv3.md @@ -0,0 +1,20 @@ +# VulnerabilityCvsSv3 + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**BaseScore** | **float32** | The base score is a function of the base metric scores. | [optional] [default to null] +**ExploitabilityScore** | **float32** | | [optional] [default to null] +**ImpactScore** | **float32** | | [optional] [default to null] +**AttackVector** | [***CvsSv3AttackVector**](CVSSv3AttackVector.md) | Base Metrics Represents the intrinsic characteristics of a vulnerability that are constant over time and across user environments. | [optional] [default to null] +**AttackComplexity** | [***CvsSv3AttackComplexity**](CVSSv3AttackComplexity.md) | | [optional] [default to null] +**PrivilegesRequired** | [***CvsSv3PrivilegesRequired**](CVSSv3PrivilegesRequired.md) | | [optional] [default to null] +**UserInteraction** | [***CvsSv3UserInteraction**](CVSSv3UserInteraction.md) | | [optional] [default to null] +**Scope** | [***CvsSv3Scope**](CVSSv3Scope.md) | | [optional] [default to null] +**ConfidentialityImpact** | [***CvsSv3Impact**](CVSSv3Impact.md) | | [optional] [default to null] +**IntegrityImpact** | [***CvsSv3Impact**](CVSSv3Impact.md) | | [optional] [default to null] +**AvailabilityImpact** | [***CvsSv3Impact**](CVSSv3Impact.md) | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/VulnerabilityDetail.md b/image-scanner/grafeas/docs/VulnerabilityDetail.md new file mode 100644 index 000000000..c5a5b3c49 --- /dev/null +++ b/image-scanner/grafeas/docs/VulnerabilityDetail.md @@ -0,0 +1,19 @@ +# VulnerabilityDetail + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**CpeUri** | **string** | Required. The CPE URI in [cpe format](https://cpe.mitre.org/specification/) in which the vulnerability manifests. Examples include distro or storage location for vulnerable jar. | [optional] [default to null] +**Package_** | **string** | Required. The name of the package where the vulnerability was found. | [optional] [default to null] +**MinAffectedVersion** | [***PackageVersion**](packageVersion.md) | The min version of the package in which the vulnerability exists. | [optional] [default to null] +**MaxAffectedVersion** | [***PackageVersion**](packageVersion.md) | The max version of the package in which the vulnerability exists. | [optional] [default to null] +**SeverityName** | **string** | The severity (eg: distro assigned severity) for this vulnerability. | [optional] [default to null] +**Description** | **string** | A vendor-specific description of this note. | [optional] [default to null] +**FixedLocation** | [***VulnerabilityVulnerabilityLocation**](vulnerabilityVulnerabilityLocation.md) | The fix for this specific package version. | [optional] [default to null] +**PackageType** | **string** | The type of package; whether native or non native(ruby gems, node.js packages etc). | [optional] [default to null] +**IsObsolete** | **bool** | Whether this detail is obsolete. Occurrences are expected not to point to obsolete details. | [optional] [default to null] +**SourceUpdateTime** | [**time.Time**](time.Time.md) | The time this information was last changed at the source. This is an upstream timestamp from the underlying information source - e.g. Ubuntu security tracker. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/VulnerabilityOccurrencesSummaryFixableTotalByDigest.md b/image-scanner/grafeas/docs/VulnerabilityOccurrencesSummaryFixableTotalByDigest.md new file mode 100644 index 000000000..ce4cced96 --- /dev/null +++ b/image-scanner/grafeas/docs/VulnerabilityOccurrencesSummaryFixableTotalByDigest.md @@ -0,0 +1,13 @@ +# VulnerabilityOccurrencesSummaryFixableTotalByDigest + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Resource** | [***V1beta1Resource**](v1beta1Resource.md) | The affected resource. | [optional] [default to null] +**Severity** | [***VulnerabilitySeverity**](vulnerabilitySeverity.md) | The severity for this count. SEVERITY_UNSPECIFIED indicates total across all severities. | [optional] [default to null] +**FixableCount** | **string** | The number of fixable vulnerabilities associated with this resource. | [optional] [default to null] +**TotalCount** | **string** | The total number of vulnerabilities associated with this resource. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/VulnerabilityPackageIssue.md b/image-scanner/grafeas/docs/VulnerabilityPackageIssue.md new file mode 100644 index 000000000..c531bc085 --- /dev/null +++ b/image-scanner/grafeas/docs/VulnerabilityPackageIssue.md @@ -0,0 +1,12 @@ +# VulnerabilityPackageIssue + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**AffectedLocation** | [***VulnerabilityVulnerabilityLocation**](vulnerabilityVulnerabilityLocation.md) | Required. The location of the vulnerability. | [optional] [default to null] +**FixedLocation** | [***VulnerabilityVulnerabilityLocation**](vulnerabilityVulnerabilityLocation.md) | The location of the available fix for vulnerability. | [optional] [default to null] +**SeverityName** | **string** | Deprecated, use Details.effective_severity instead The severity (e.g., distro assigned severity) for this vulnerability. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/VulnerabilitySeverity.md b/image-scanner/grafeas/docs/VulnerabilitySeverity.md new file mode 100644 index 000000000..16d719a93 --- /dev/null +++ b/image-scanner/grafeas/docs/VulnerabilitySeverity.md @@ -0,0 +1,9 @@ +# VulnerabilitySeverity + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/VulnerabilityVulnerability.md b/image-scanner/grafeas/docs/VulnerabilityVulnerability.md new file mode 100644 index 000000000..3f74dd094 --- /dev/null +++ b/image-scanner/grafeas/docs/VulnerabilityVulnerability.md @@ -0,0 +1,15 @@ +# VulnerabilityVulnerability + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**CvssScore** | **float32** | The CVSS score for this vulnerability. | [optional] [default to null] +**Severity** | [***VulnerabilitySeverity**](vulnerabilitySeverity.md) | Note provider assigned impact of the vulnerability. | [optional] [default to null] +**Details** | [**[]VulnerabilityDetail**](VulnerabilityDetail.md) | All information about the package to specifically identify this vulnerability. One entry per (version range and cpe_uri) the package vulnerability has manifested in. | [optional] [default to null] +**CvssV3** | [***VulnerabilityCvsSv3**](vulnerabilityCVSSv3.md) | The full description of the CVSSv3. | [optional] [default to null] +**WindowsDetails** | [**[]VulnerabilityWindowsDetail**](VulnerabilityWindowsDetail.md) | Windows details get their own format because the information format and model don't match a normal detail. Specifically Windows updates are done as patches, thus Windows vulnerabilities really are a missing package, rather than a package being at an incorrect version. | [optional] [default to null] +**SourceUpdateTime** | [**time.Time**](time.Time.md) | The time this information was last changed at the source. This is an upstream timestamp from the underlying information source - e.g. Ubuntu security tracker. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/VulnerabilityVulnerabilityLocation.md b/image-scanner/grafeas/docs/VulnerabilityVulnerabilityLocation.md new file mode 100644 index 000000000..bee8b220c --- /dev/null +++ b/image-scanner/grafeas/docs/VulnerabilityVulnerabilityLocation.md @@ -0,0 +1,12 @@ +# VulnerabilityVulnerabilityLocation + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**CpeUri** | **string** | Required. The CPE URI in [cpe format](https://cpe.mitre.org/specification/) format. Examples include distro or storage location for vulnerable jar. | [optional] [default to null] +**Package_** | **string** | Required. The package being described. | [optional] [default to null] +**Version** | [***PackageVersion**](packageVersion.md) | Required. The version of the package being described. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/VulnerabilityWindowsDetail.md b/image-scanner/grafeas/docs/VulnerabilityWindowsDetail.md new file mode 100644 index 000000000..cb9b9ebcf --- /dev/null +++ b/image-scanner/grafeas/docs/VulnerabilityWindowsDetail.md @@ -0,0 +1,13 @@ +# VulnerabilityWindowsDetail + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**CpeUri** | **string** | Required. The CPE URI in [cpe format](https://cpe.mitre.org/specification/) in which the vulnerability manifests. Examples include distro or storage location for vulnerable jar. | [optional] [default to null] +**Name** | **string** | Required. The name of the vulnerability. | [optional] [default to null] +**Description** | **string** | The description of the vulnerability. | [optional] [default to null] +**FixingKbs** | [**[]WindowsDetailKnowledgeBase**](WindowsDetailKnowledgeBase.md) | Required. The names of the KBs which have hotfixes to mitigate this vulnerability. Note that there may be multiple hotfixes (and thus multiple KBs) that mitigate a given vulnerability. Currently any listed kb's presence is considered a fix. | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/docs/WindowsDetailKnowledgeBase.md b/image-scanner/grafeas/docs/WindowsDetailKnowledgeBase.md new file mode 100644 index 000000000..f23394769 --- /dev/null +++ b/image-scanner/grafeas/docs/WindowsDetailKnowledgeBase.md @@ -0,0 +1,11 @@ +# WindowsDetailKnowledgeBase + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Name** | **string** | The KB name (generally of the form KB[0-9]+ i.e. KB123456). | [optional] [default to null] +**Url** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/image-scanner/grafeas/git_push.sh b/image-scanner/grafeas/git_push.sh new file mode 100644 index 000000000..a21e3ea92 --- /dev/null +++ b/image-scanner/grafeas/git_push.sh @@ -0,0 +1,68 @@ +#!/bin/sh +# +# Copyright (c) 2024. Devtron Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 swagger-petstore-perl "minor update" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/image-scanner/grafeas/model_alias_context_kind.go b/image-scanner/grafeas/model_alias_context_kind.go new file mode 100644 index 000000000..c87f60065 --- /dev/null +++ b/image-scanner/grafeas/model_alias_context_kind.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas +// AliasContextKind : The type of an alias. - KIND_UNSPECIFIED: Unknown. - FIXED: Git tag. - MOVABLE: Git branch. - OTHER: Used to specify non-standard aliases. For example, if a Git repo has a ref named \"refs/foo/bar\". +type AliasContextKind string + +// List of AliasContextKind +const ( + KIND_UNSPECIFIED_AliasContextKind AliasContextKind = "KIND_UNSPECIFIED" + FIXED_AliasContextKind AliasContextKind = "FIXED" + MOVABLE_AliasContextKind AliasContextKind = "MOVABLE" + OTHER_AliasContextKind AliasContextKind = "OTHER" +) diff --git a/image-scanner/grafeas/model_attestation_attestation.go b/image-scanner/grafeas/model_attestation_attestation.go new file mode 100644 index 000000000..22483532c --- /dev/null +++ b/image-scanner/grafeas/model_attestation_attestation.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Occurrence that represents a single \"attestation\". The authenticity of an attestation can be verified using the attached signature. If the verifier trusts the public key of the signer, then verifying the signature is sufficient to establish trust. In this circumstance, the authority to which this attestation is attached is primarily useful for look-up (how to find this attestation if you already know the authority and artifact to be verified) and intent (which authority was this attestation intended to sign for). +type AttestationAttestation struct { + // A PGP signed attestation. + PgpSignedAttestation *AttestationPgpSignedAttestation `json:"pgp_signed_attestation,omitempty"` + // An attestation that supports multiple `Signature`s over the same attestation payload. The signatures (defined in common.proto) support a superset of public key types and IDs compared to PgpSignedAttestation. + GenericSignedAttestation *AttestationGenericSignedAttestation `json:"generic_signed_attestation,omitempty"` +} diff --git a/image-scanner/grafeas/model_attestation_authority.go b/image-scanner/grafeas/model_attestation_authority.go new file mode 100644 index 000000000..2655e407a --- /dev/null +++ b/image-scanner/grafeas/model_attestation_authority.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Note kind that represents a logical attestation \"role\" or \"authority\". For example, an organization might have one `Authority` for \"QA\" and one for \"build\". This note is intended to act strictly as a grouping mechanism for the attached occurrences (Attestations). This grouping mechanism also provides a security boundary, since IAM ACLs gate the ability for a principle to attach an occurrence to a given note. It also provides a single point of lookup to find all attached attestation occurrences, even if they don't all live in the same project. +type AttestationAuthority struct { + // Hint hints at the purpose of the attestation authority. + Hint *AuthorityHint `json:"hint,omitempty"` +} diff --git a/image-scanner/grafeas/model_attestation_generic_signed_attestation.go b/image-scanner/grafeas/model_attestation_generic_signed_attestation.go new file mode 100644 index 000000000..e38a69801 --- /dev/null +++ b/image-scanner/grafeas/model_attestation_generic_signed_attestation.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// An attestation wrapper that uses the Grafeas `Signature` message. This attestation must define the `serialized_payload` that the `signatures` verify and any metadata necessary to interpret that plaintext. The signatures should always be over the `serialized_payload` bytestring. +type AttestationGenericSignedAttestation struct { + // Type (for example schema) of the attestation payload that was signed. The verifier must ensure that the provided type is one that the verifier supports, and that the attestation payload is a valid instantiation of that type (for example by validating a JSON schema). + ContentType *AttestationGenericSignedAttestationContentType `json:"content_type,omitempty"` + // The serialized payload that is verified by one or more `signatures`. The encoding and semantic meaning of this payload must match what is set in `content_type`. + SerializedPayload string `json:"serialized_payload,omitempty"` + // One or more signatures over `serialized_payload`. Verifier implementations should consider this attestation message verified if at least one `signature` verifies `serialized_payload`. See `Signature` in common.proto for more details on signature structure and verification. + Signatures []Grafeasv1beta1Signature `json:"signatures,omitempty"` +} diff --git a/image-scanner/grafeas/model_attestation_generic_signed_attestation_content_type.go b/image-scanner/grafeas/model_attestation_generic_signed_attestation_content_type.go new file mode 100644 index 000000000..d446ceed6 --- /dev/null +++ b/image-scanner/grafeas/model_attestation_generic_signed_attestation_content_type.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas +// AttestationGenericSignedAttestationContentType : Type of the attestation plaintext that was signed. - CONTENT_TYPE_UNSPECIFIED: `ContentType` is not set. - SIMPLE_SIGNING_JSON: Atomic format attestation signature. See https://github.com/containers/image/blob/8a5d2f82a6e3263290c8e0276c3e0f64e77723e7/docs/atomic-signature.md The payload extracted in `plaintext` is a JSON blob conforming to the linked schema. +type AttestationGenericSignedAttestationContentType string + +// List of attestationGenericSignedAttestationContentType +const ( + CONTENT_TYPE_UNSPECIFIED_AttestationGenericSignedAttestationContentType AttestationGenericSignedAttestationContentType = "CONTENT_TYPE_UNSPECIFIED" + SIMPLE_SIGNING_JSON_AttestationGenericSignedAttestationContentType AttestationGenericSignedAttestationContentType = "SIMPLE_SIGNING_JSON" +) diff --git a/image-scanner/grafeas/model_attestation_pgp_signed_attestation.go b/image-scanner/grafeas/model_attestation_pgp_signed_attestation.go new file mode 100644 index 000000000..e8b2eb0ab --- /dev/null +++ b/image-scanner/grafeas/model_attestation_pgp_signed_attestation.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// An attestation wrapper with a PGP-compatible signature. This message only supports `ATTACHED` signatures, where the payload that is signed is included alongside the signature itself in the same file. +type AttestationPgpSignedAttestation struct { + // Required. The raw content of the signature, as output by GNU Privacy Guard (GPG) or equivalent. Since this message only supports attached signatures, the payload that was signed must be attached. While the signature format supported is dependent on the verification implementation, currently only ASCII-armored (`--armor` to gpg), non-clearsigned (`--sign` rather than `--clearsign` to gpg) are supported. Concretely, `gpg --sign --armor --output=signature.gpg payload.json` will create the signature content expected in this field in `signature.gpg` for the `payload.json` attestation payload. + Signature string `json:"signature,omitempty"` + // Type (for example schema) of the attestation payload that was signed. The verifier must ensure that the provided type is one that the verifier supports, and that the attestation payload is a valid instantiation of that type (for example by validating a JSON schema). + ContentType *AttestationPgpSignedAttestationContentType `json:"content_type,omitempty"` + // The cryptographic fingerprint of the key used to generate the signature, as output by, e.g. `gpg --list-keys`. This should be the version 4, full 160-bit fingerprint, expressed as a 40 character hexidecimal string. See https://tools.ietf.org/html/rfc4880#section-12.2 for details. Implementations may choose to acknowledge \"LONG\", \"SHORT\", or other abbreviated key IDs, but only the full fingerprint is guaranteed to work. In gpg, the full fingerprint can be retrieved from the `fpr` field returned when calling --list-keys with --with-colons. For example: ``` gpg --with-colons --with-fingerprint --force-v4-certs \\ --list-keys attester@example.com tru::1:1513631572:0:3:1:5 pub:...... fpr:::::::::24FF6481B76AC91E66A00AC657A93A81EF3AE6FB: ``` Above, the fingerprint is `24FF6481B76AC91E66A00AC657A93A81EF3AE6FB`. + PgpKeyId string `json:"pgp_key_id,omitempty"` +} diff --git a/image-scanner/grafeas/model_attestation_pgp_signed_attestation_content_type.go b/image-scanner/grafeas/model_attestation_pgp_signed_attestation_content_type.go new file mode 100644 index 000000000..cd2284430 --- /dev/null +++ b/image-scanner/grafeas/model_attestation_pgp_signed_attestation_content_type.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas +// AttestationPgpSignedAttestationContentType : Type (for example schema) of the attestation payload that was signed. - CONTENT_TYPE_UNSPECIFIED: `ContentType` is not set. - SIMPLE_SIGNING_JSON: Atomic format attestation signature. See https://github.com/containers/image/blob/8a5d2f82a6e3263290c8e0276c3e0f64e77723e7/docs/atomic-signature.md The payload extracted from `signature` is a JSON blob conforming to the linked schema. +type AttestationPgpSignedAttestationContentType string + +// List of attestationPgpSignedAttestationContentType +const ( + CONTENT_TYPE_UNSPECIFIED_AttestationPgpSignedAttestationContentType AttestationPgpSignedAttestationContentType = "CONTENT_TYPE_UNSPECIFIED" + SIMPLE_SIGNING_JSON_AttestationPgpSignedAttestationContentType AttestationPgpSignedAttestationContentType = "SIMPLE_SIGNING_JSON" +) diff --git a/image-scanner/grafeas/model_authority_hint.go b/image-scanner/grafeas/model_authority_hint.go new file mode 100644 index 000000000..a28b26051 --- /dev/null +++ b/image-scanner/grafeas/model_authority_hint.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// This submessage provides human-readable hints about the purpose of the authority. Because the name of a note acts as its resource reference, it is important to disambiguate the canonical name of the Note (which might be a UUID for security purposes) from \"readable\" names more suitable for debug output. Note that these hints should not be used to look up authorities in security sensitive contexts, such as when looking up attestations to verify. +type AuthorityHint struct { + // Required. The human readable name of this attestation authority, for example \"qa\". + HumanReadableName string `json:"humanReadableName,omitempty"` +} diff --git a/image-scanner/grafeas/model_build_build.go b/image-scanner/grafeas/model_build_build.go new file mode 100644 index 000000000..921076b20 --- /dev/null +++ b/image-scanner/grafeas/model_build_build.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Note holding the version of the provider's builder and the signature of the provenance message in the build details occurrence. +type BuildBuild struct { + // Required. Immutable. Version of the builder which produced this build. + BuilderVersion string `json:"builderVersion,omitempty"` + // Signature of the build in occurrences pointing to this build note containing build details. + Signature *BuildBuildSignature `json:"signature,omitempty"` +} diff --git a/image-scanner/grafeas/model_build_build_signature.go b/image-scanner/grafeas/model_build_build_signature.go new file mode 100644 index 000000000..fcba53255 --- /dev/null +++ b/image-scanner/grafeas/model_build_build_signature.go @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Message encapsulating the signature of the verified build. +type BuildBuildSignature struct { + // Public key of the builder which can be used to verify that the related findings are valid and unchanged. If `key_type` is empty, this defaults to PEM encoded public keys. This field may be empty if `key_id` references an external key. For Cloud Build based signatures, this is a PEM encoded public key. To verify the Cloud Build signature, place the contents of this field into a file (public.pem). The signature field is base64-decoded into its binary representation in signature.bin, and the provenance bytes from `BuildDetails` are base64-decoded into a binary representation in signed.bin. OpenSSL can then verify the signature: `openssl sha256 -verify public.pem -signature signature.bin signed.bin` + PublicKey string `json:"publicKey,omitempty"` + // Required. Signature of the related `BuildProvenance`. In JSON, this is base-64 encoded. + Signature string `json:"signature,omitempty"` + // An ID for the key used to sign. This could be either an ID for the key stored in `public_key` (such as the ID or fingerprint for a PGP key, or the CN for a cert), or a reference to an external key (such as a reference to a key in Cloud Key Management Service). + KeyId string `json:"keyId,omitempty"` + // The type of the key, either stored in `public_key` or referenced in `key_id`. + KeyType *BuildSignatureKeyType `json:"keyType,omitempty"` +} diff --git a/image-scanner/grafeas/model_build_signature_key_type.go b/image-scanner/grafeas/model_build_signature_key_type.go new file mode 100644 index 000000000..659d9c0f2 --- /dev/null +++ b/image-scanner/grafeas/model_build_signature_key_type.go @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas +// BuildSignatureKeyType : Public key formats. - KEY_TYPE_UNSPECIFIED: `KeyType` is not set. - PGP_ASCII_ARMORED: `PGP ASCII Armored` public key. - PKIX_PEM: `PKIX PEM` public key. +type BuildSignatureKeyType string + +// List of BuildSignatureKeyType +const ( + KEY_TYPE_UNSPECIFIED_BuildSignatureKeyType BuildSignatureKeyType = "KEY_TYPE_UNSPECIFIED" + PGP_ASCII_ARMORED_BuildSignatureKeyType BuildSignatureKeyType = "PGP_ASCII_ARMORED" + PKIX_PEM_BuildSignatureKeyType BuildSignatureKeyType = "PKIX_PEM" +) diff --git a/image-scanner/grafeas/model_cvs_sv3_attack_complexity.go b/image-scanner/grafeas/model_cvs_sv3_attack_complexity.go new file mode 100644 index 000000000..d79b97f18 --- /dev/null +++ b/image-scanner/grafeas/model_cvs_sv3_attack_complexity.go @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +type CvsSv3AttackComplexity string + +// List of CVSSv3AttackComplexity +const ( + UNSPECIFIED_CvsSv3AttackComplexity CvsSv3AttackComplexity = "ATTACK_COMPLEXITY_UNSPECIFIED" + LOW_CvsSv3AttackComplexity CvsSv3AttackComplexity = "ATTACK_COMPLEXITY_LOW" + HIGH_CvsSv3AttackComplexity CvsSv3AttackComplexity = "ATTACK_COMPLEXITY_HIGH" +) diff --git a/image-scanner/grafeas/model_cvs_sv3_attack_vector.go b/image-scanner/grafeas/model_cvs_sv3_attack_vector.go new file mode 100644 index 000000000..77923540d --- /dev/null +++ b/image-scanner/grafeas/model_cvs_sv3_attack_vector.go @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +type CvsSv3AttackVector string + +// List of CVSSv3AttackVector +const ( + UNSPECIFIED_CvsSv3AttackVector CvsSv3AttackVector = "ATTACK_VECTOR_UNSPECIFIED" + NETWORK_CvsSv3AttackVector CvsSv3AttackVector = "ATTACK_VECTOR_NETWORK" + ADJACENT_CvsSv3AttackVector CvsSv3AttackVector = "ATTACK_VECTOR_ADJACENT" + LOCAL_CvsSv3AttackVector CvsSv3AttackVector = "ATTACK_VECTOR_LOCAL" + PHYSICAL_CvsSv3AttackVector CvsSv3AttackVector = "ATTACK_VECTOR_PHYSICAL" +) diff --git a/image-scanner/grafeas/model_cvs_sv3_impact.go b/image-scanner/grafeas/model_cvs_sv3_impact.go new file mode 100644 index 000000000..b5620545e --- /dev/null +++ b/image-scanner/grafeas/model_cvs_sv3_impact.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +type CvsSv3Impact string + +// List of CVSSv3Impact +const ( + UNSPECIFIED_CvsSv3Impact CvsSv3Impact = "IMPACT_UNSPECIFIED" + HIGH_CvsSv3Impact CvsSv3Impact = "IMPACT_HIGH" + LOW_CvsSv3Impact CvsSv3Impact = "IMPACT_LOW" + NONE_CvsSv3Impact CvsSv3Impact = "IMPACT_NONE" +) diff --git a/image-scanner/grafeas/model_cvs_sv3_privileges_required.go b/image-scanner/grafeas/model_cvs_sv3_privileges_required.go new file mode 100644 index 000000000..926f09303 --- /dev/null +++ b/image-scanner/grafeas/model_cvs_sv3_privileges_required.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +type CvsSv3PrivilegesRequired string + +// List of CVSSv3PrivilegesRequired +const ( + UNSPECIFIED_CvsSv3PrivilegesRequired CvsSv3PrivilegesRequired = "PRIVILEGES_REQUIRED_UNSPECIFIED" + NONE_CvsSv3PrivilegesRequired CvsSv3PrivilegesRequired = "PRIVILEGES_REQUIRED_NONE" + LOW_CvsSv3PrivilegesRequired CvsSv3PrivilegesRequired = "PRIVILEGES_REQUIRED_LOW" + HIGH_CvsSv3PrivilegesRequired CvsSv3PrivilegesRequired = "PRIVILEGES_REQUIRED_HIGH" +) diff --git a/image-scanner/grafeas/model_cvs_sv3_scope.go b/image-scanner/grafeas/model_cvs_sv3_scope.go new file mode 100644 index 000000000..a9ed77be2 --- /dev/null +++ b/image-scanner/grafeas/model_cvs_sv3_scope.go @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +type CvsSv3Scope string + +// List of CVSSv3Scope +const ( + UNSPECIFIED_CvsSv3Scope CvsSv3Scope = "SCOPE_UNSPECIFIED" + UNCHANGED_CvsSv3Scope CvsSv3Scope = "SCOPE_UNCHANGED" + CHANGED_CvsSv3Scope CvsSv3Scope = "SCOPE_CHANGED" +) diff --git a/image-scanner/grafeas/model_cvs_sv3_user_interaction.go b/image-scanner/grafeas/model_cvs_sv3_user_interaction.go new file mode 100644 index 000000000..9673f55a3 --- /dev/null +++ b/image-scanner/grafeas/model_cvs_sv3_user_interaction.go @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +type CvsSv3UserInteraction string + +// List of CVSSv3UserInteraction +const ( + UNSPECIFIED_CvsSv3UserInteraction CvsSv3UserInteraction = "USER_INTERACTION_UNSPECIFIED" + NONE_CvsSv3UserInteraction CvsSv3UserInteraction = "USER_INTERACTION_NONE" + REQUIRED_CvsSv3UserInteraction CvsSv3UserInteraction = "USER_INTERACTION_REQUIRED" +) diff --git a/image-scanner/grafeas/model_deployment_deployable.go b/image-scanner/grafeas/model_deployment_deployable.go new file mode 100644 index 000000000..a339adca3 --- /dev/null +++ b/image-scanner/grafeas/model_deployment_deployable.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// An artifact that can be deployed in some runtime. +type DeploymentDeployable struct { + // Required. Resource URI for the artifact being deployed. + ResourceUri []string `json:"resourceUri,omitempty"` +} diff --git a/image-scanner/grafeas/model_deployment_deployment.go b/image-scanner/grafeas/model_deployment_deployment.go new file mode 100644 index 000000000..21efd0d91 --- /dev/null +++ b/image-scanner/grafeas/model_deployment_deployment.go @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +import ( + "time" +) + +// The period during which some deployable was active in a runtime. +type DeploymentDeployment struct { + // Identity of the user that triggered this deployment. + UserEmail string `json:"user_email,omitempty"` + // Required. Beginning of the lifetime of this deployment. + DeployTime time.Time `json:"deploy_time,omitempty"` + // End of the lifetime of this deployment. + UndeployTime time.Time `json:"undeploy_time,omitempty"` + // Configuration used to create this deployment. + Config string `json:"config,omitempty"` + // Address of the runtime element hosting this deployment. + Address string `json:"address,omitempty"` + // Output only. Resource URI for the artifact being deployed taken from the deployable field with the same name. + ResourceUri []string `json:"resource_uri,omitempty"` + // Platform hosting this deployment. + Platform *DeploymentPlatform `json:"platform,omitempty"` +} diff --git a/image-scanner/grafeas/model_deployment_platform.go b/image-scanner/grafeas/model_deployment_platform.go new file mode 100644 index 000000000..4614ee921 --- /dev/null +++ b/image-scanner/grafeas/model_deployment_platform.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas +// DeploymentPlatform : Types of platforms. - PLATFORM_UNSPECIFIED: Unknown. - GKE: Google Container Engine. - FLEX: Google App Engine: Flexible Environment. - CUSTOM: Custom user-defined platform. +type DeploymentPlatform string + +// List of DeploymentPlatform +const ( + PLATFORM_UNSPECIFIED_DeploymentPlatform DeploymentPlatform = "PLATFORM_UNSPECIFIED" + GKE_DeploymentPlatform DeploymentPlatform = "GKE" + FLEX_DeploymentPlatform DeploymentPlatform = "FLEX" + CUSTOM_DeploymentPlatform DeploymentPlatform = "CUSTOM" +) diff --git a/image-scanner/grafeas/model_discovered_analysis_status.go b/image-scanner/grafeas/model_discovered_analysis_status.go new file mode 100644 index 000000000..d99a7e2a6 --- /dev/null +++ b/image-scanner/grafeas/model_discovered_analysis_status.go @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas +// DiscoveredAnalysisStatus : Analysis status for a resource. Currently for initial analysis only (not updated in continuous analysis). - ANALYSIS_STATUS_UNSPECIFIED: Unknown. - PENDING: Resource is known but no action has been taken yet. - SCANNING: Resource is being analyzed. - FINISHED_SUCCESS: Analysis has finished successfully. - FINISHED_FAILED: Analysis has finished unsuccessfully, the analysis itself is in a bad state. - FINISHED_UNSUPPORTED: The resource is known not to be supported +type DiscoveredAnalysisStatus string + +// List of DiscoveredAnalysisStatus +const ( + ANALYSIS_STATUS_UNSPECIFIED_DiscoveredAnalysisStatus DiscoveredAnalysisStatus = "ANALYSIS_STATUS_UNSPECIFIED" + PENDING_DiscoveredAnalysisStatus DiscoveredAnalysisStatus = "PENDING" + SCANNING_DiscoveredAnalysisStatus DiscoveredAnalysisStatus = "SCANNING" + FINISHED_SUCCESS_DiscoveredAnalysisStatus DiscoveredAnalysisStatus = "FINISHED_SUCCESS" + FINISHED_FAILED_DiscoveredAnalysisStatus DiscoveredAnalysisStatus = "FINISHED_FAILED" + FINISHED_UNSUPPORTED_DiscoveredAnalysisStatus DiscoveredAnalysisStatus = "FINISHED_UNSUPPORTED" +) diff --git a/image-scanner/grafeas/model_discovered_continuous_analysis.go b/image-scanner/grafeas/model_discovered_continuous_analysis.go new file mode 100644 index 000000000..2bc509c87 --- /dev/null +++ b/image-scanner/grafeas/model_discovered_continuous_analysis.go @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas +// DiscoveredContinuousAnalysis : Whether the resource is continuously analyzed. - CONTINUOUS_ANALYSIS_UNSPECIFIED: Unknown. - ACTIVE: The resource is continuously analyzed. - INACTIVE: The resource is ignored for continuous analysis. +type DiscoveredContinuousAnalysis string + +// List of DiscoveredContinuousAnalysis +const ( + CONTINUOUS_ANALYSIS_UNSPECIFIED_DiscoveredContinuousAnalysis DiscoveredContinuousAnalysis = "CONTINUOUS_ANALYSIS_UNSPECIFIED" + ACTIVE_DiscoveredContinuousAnalysis DiscoveredContinuousAnalysis = "ACTIVE" + INACTIVE_DiscoveredContinuousAnalysis DiscoveredContinuousAnalysis = "INACTIVE" +) diff --git a/image-scanner/grafeas/model_discovery_discovered.go b/image-scanner/grafeas/model_discovery_discovered.go new file mode 100644 index 000000000..4d5627429 --- /dev/null +++ b/image-scanner/grafeas/model_discovery_discovered.go @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +import ( + "time" +) + +// Provides information about the analysis status of a discovered resource. +type DiscoveryDiscovered struct { + // Whether the resource is continuously analyzed. + ContinuousAnalysis *DiscoveredContinuousAnalysis `json:"continuous_analysis,omitempty"` + // The last time continuous analysis was done for this resource. Deprecated, do not use. + LastAnalysisTime time.Time `json:"last_analysis_time,omitempty"` + // The status of discovery for the resource. + AnalysisStatus *DiscoveredAnalysisStatus `json:"analysis_status,omitempty"` + // When an error is encountered this will contain a LocalizedMessage under details to show to the user. The LocalizedMessage is output only and populated by the API. + AnalysisStatusError *RpcStatus `json:"analysis_status_error,omitempty"` +} diff --git a/image-scanner/grafeas/model_discovery_discovery.go b/image-scanner/grafeas/model_discovery_discovery.go new file mode 100644 index 000000000..a3ac841f2 --- /dev/null +++ b/image-scanner/grafeas/model_discovery_discovery.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// A note that indicates a type of analysis a provider would perform. This note exists in a provider's project. A `Discovery` occurrence is created in a consumer's project at the start of analysis. +type DiscoveryDiscovery struct { + // Required. Immutable. The kind of analysis that is handled by this discovery. + AnalysisKind *V1beta1NoteKind `json:"analysisKind,omitempty"` +} diff --git a/image-scanner/grafeas/model_grafeasv1beta1_signature.go b/image-scanner/grafeas/model_grafeasv1beta1_signature.go new file mode 100644 index 000000000..441ac3b12 --- /dev/null +++ b/image-scanner/grafeas/model_grafeasv1beta1_signature.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Verifiers (e.g. Kritis implementations) MUST verify signatures with respect to the trust anchors defined in policy (e.g. a Kritis policy). Typically this means that the verifier has been configured with a map from `public_key_id` to public key material (and any required parameters, e.g. signing algorithm). In particular, verification implementations MUST NOT treat the signature `public_key_id` as anything more than a key lookup hint. The `public_key_id` DOES NOT validate or authenticate a public key; it only provides a mechanism for quickly selecting a public key ALREADY CONFIGURED on the verifier through a trusted channel. Verification implementations MUST reject signatures in any of the following circumstances: * The `public_key_id` is not recognized by the verifier. * The public key that `public_key_id` refers to does not verify the signature with respect to the payload. The `signature` contents SHOULD NOT be \"attached\" (where the payload is included with the serialized `signature` bytes). Verifiers MUST ignore any \"attached\" payload and only verify signatures with respect to explicitly provided payload (e.g. a `payload` field on the proto message that holds this Signature, or the canonical serialization of the proto message that holds this signature). +type Grafeasv1beta1Signature struct { + // The content of the signature, an opaque bytestring. The payload that this signature verifies MUST be unambiguously provided with the Signature during verification. A wrapper message might provide the payload explicitly. Alternatively, a message might have a canonical serialization that can always be unambiguously computed to derive the payload. + Signature string `json:"signature,omitempty"` + // The identifier for the public key that verifies this signature. * The `public_key_id` is required. * The `public_key_id` SHOULD be an RFC3986 conformant URI. * When possible, the `public_key_id` SHOULD be an immutable reference, such as a cryptographic digest. Examples of valid `public_key_id`s: OpenPGP V4 public key fingerprint: * \"openpgp4fpr:74FAF3B861BDA0870C7B6DEF607E48D2A663AEEA\" See https://www.iana.org/assignments/uri-schemes/prov/openpgp4fpr for more details on this scheme. RFC6920 digest-named SubjectPublicKeyInfo (digest of the DER serialization): * \"ni:///sha-256;cD9o9Cq6LG3jD0iKXqEi_vdjJGecm_iXkbqVoScViaU\" * \"nih:///sha-256;703f68f42aba2c6de30f488a5ea122fef76324679c9bf89791ba95a1271589a5\" + PublicKeyId string `json:"public_key_id,omitempty"` +} diff --git a/image-scanner/grafeas/model_hash_hash_type.go b/image-scanner/grafeas/model_hash_hash_type.go new file mode 100644 index 000000000..2f79ebe49 --- /dev/null +++ b/image-scanner/grafeas/model_hash_hash_type.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas +// HashHashType : Specifies the hash algorithm. - HASH_TYPE_UNSPECIFIED: Unknown. - SHA256: A SHA-256 hash. +type HashHashType string + +// List of HashHashType +const ( + HASH_TYPE_UNSPECIFIED_HashHashType HashHashType = "HASH_TYPE_UNSPECIFIED" + SHA256_HashHashType HashHashType = "SHA256" +) diff --git a/image-scanner/grafeas/model_image_basis.go b/image-scanner/grafeas/model_image_basis.go new file mode 100644 index 000000000..f759203c1 --- /dev/null +++ b/image-scanner/grafeas/model_image_basis.go @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Basis describes the base image portion (Note) of the DockerImage relationship. Linked occurrences are derived from this or an equivalent image via: FROM Or an equivalent reference, e.g. a tag of the resource_url. +type ImageBasis struct { + // Required. Immutable. The resource_url for the resource representing the basis of associated occurrence images. + ResourceUrl string `json:"resourceUrl,omitempty"` + // Required. Immutable. The fingerprint of the base image. + Fingerprint *ImageFingerprint `json:"fingerprint,omitempty"` +} + diff --git a/image-scanner/grafeas/model_image_derived.go b/image-scanner/grafeas/model_image_derived.go new file mode 100644 index 000000000..5d4cff2d8 --- /dev/null +++ b/image-scanner/grafeas/model_image_derived.go @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Derived describes the derived image portion (Occurrence) of the DockerImage relationship. This image would be produced from a Dockerfile with FROM . +type ImageDerived struct { + // Required. The fingerprint of the derived image. + Fingerprint *ImageFingerprint `json:"fingerprint,omitempty"` + // Output only. The number of layers by which this image differs from the associated image basis. + Distance int32 `json:"distance,omitempty"` + // This contains layer-specific metadata, if populated it has length \"distance\" and is ordered with [distance] being the layer immediately following the base image and [1] being the final layer. + LayerInfo []ImageLayer `json:"layer_info,omitempty"` + // Output only. This contains the base image URL for the derived image occurrence. + BaseResourceUrl string `json:"base_resource_url,omitempty"` +} diff --git a/image-scanner/grafeas/model_image_fingerprint.go b/image-scanner/grafeas/model_image_fingerprint.go new file mode 100644 index 000000000..3ab164fc3 --- /dev/null +++ b/image-scanner/grafeas/model_image_fingerprint.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// A set of properties that uniquely identify a given Docker image. +type ImageFingerprint struct { + // Required. The layer ID of the final layer in the Docker image's v1 representation. + V1Name string `json:"v1Name,omitempty"` + // Required. The ordered list of v2 blobs that represent a given image. + V2Blob []string `json:"v2Blob,omitempty"` + // Output only. The name of the image's v2 blobs computed via: [bottom] := v2_blob[bottom] [N] := sha256(v2_blob[N] + \" \" + v2_name[N+1]) Only the name of the final blob is kept. + V2Name string `json:"v2Name,omitempty"` +} diff --git a/image-scanner/grafeas/model_image_layer.go b/image-scanner/grafeas/model_image_layer.go new file mode 100644 index 000000000..4937c499b --- /dev/null +++ b/image-scanner/grafeas/model_image_layer.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Layer holds metadata specific to a layer of a Docker image. +type ImageLayer struct { + // Required. The recovered Dockerfile directive used to construct this layer. + Directive *LayerDirective `json:"directive,omitempty"` + // The recovered arguments to the Dockerfile directive. + Arguments string `json:"arguments,omitempty"` +} diff --git a/image-scanner/grafeas/model_in_toto_artifact_rule.go b/image-scanner/grafeas/model_in_toto_artifact_rule.go new file mode 100644 index 000000000..5457650c9 --- /dev/null +++ b/image-scanner/grafeas/model_in_toto_artifact_rule.go @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +type InTotoArtifactRule struct { + ArtifactRule []string `json:"artifactRule,omitempty"` +} diff --git a/image-scanner/grafeas/model_intoto_in_toto.go b/image-scanner/grafeas/model_intoto_in_toto.go new file mode 100644 index 000000000..82bf0f161 --- /dev/null +++ b/image-scanner/grafeas/model_intoto_in_toto.go @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// This contains the fields corresponding to the definition of a software supply chain step in an in-toto layout. This information goes into a Grafeas note. +type IntotoInToto struct { + // This field identifies the name of the step in the supply chain. + StepName string `json:"stepName,omitempty"` + // This field contains the public keys that can be used to verify the signatures on the step metadata. + SigningKeys []IntotoSigningKey `json:"signingKeys,omitempty"` + // The following fields contain in-toto artifact rules identifying the artifacts that enter this supply chain step, and exit the supply chain step, i.e. materials and products of the step. + ExpectedMaterials []InTotoArtifactRule `json:"expectedMaterials,omitempty"` + ExpectedProducts []InTotoArtifactRule `json:"expectedProducts,omitempty"` + // This field contains the expected command used to perform the step. + ExpectedCommand []string `json:"expectedCommand,omitempty"` + // This field contains a value that indicates the minimum number of keys that need to be used to sign the step's in-toto link. + Threshold string `json:"threshold,omitempty"` +} diff --git a/image-scanner/grafeas/model_intoto_link.go b/image-scanner/grafeas/model_intoto_link.go new file mode 100644 index 000000000..0ac9f4d3b --- /dev/null +++ b/image-scanner/grafeas/model_intoto_link.go @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// This corresponds to an in-toto link. +type IntotoLink struct { + EffectiveCommand []string `json:"effective_command,omitempty"` + Materials []IntotoLinkArtifact `json:"materials,omitempty"` + // Products are the supply chain artifacts generated as a result of the step. The structure is identical to that of materials. + Products []IntotoLinkArtifact `json:"products,omitempty"` + // ByProducts are data generated as part of a software supply chain step, but are not the actual result of the step. + Byproducts *LinkByProducts `json:"byproducts,omitempty"` + Environment *LinkEnvironment `json:"environment,omitempty"` +} diff --git a/image-scanner/grafeas/model_intoto_link_artifact.go b/image-scanner/grafeas/model_intoto_link_artifact.go new file mode 100644 index 000000000..11d15f772 --- /dev/null +++ b/image-scanner/grafeas/model_intoto_link_artifact.go @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +type IntotoLinkArtifact struct { + ResourceUri string `json:"resource_uri,omitempty"` + Hashes *LinkArtifactHashes `json:"hashes,omitempty"` +} diff --git a/image-scanner/grafeas/model_intoto_signing_key.go b/image-scanner/grafeas/model_intoto_signing_key.go new file mode 100644 index 000000000..061733202 --- /dev/null +++ b/image-scanner/grafeas/model_intoto_signing_key.go @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// This defines the format used to record keys used in the software supply chain. An in-toto link is attested using one or more keys defined in the in-toto layout. An example of this is: { \"key_id\": \"776a00e29f3559e0141b3b096f696abc6cfb0c657ab40f441132b345b0...\", \"key_type\": \"rsa\", \"public_key_value\": \"-----BEGIN PUBLIC KEY-----\\nMIIBojANBgkqhkiG9w0B...\", \"key_scheme\": \"rsassa-pss-sha256\" } The format for in-toto's key definition can be found in section 4.2 of the in-toto specification. +type IntotoSigningKey struct { + // key_id is an identifier for the signing key. + KeyId string `json:"keyId,omitempty"` + // This field identifies the specific signing method. Eg: \"rsa\", \"ed25519\", and \"ecdsa\". + KeyType string `json:"keyType,omitempty"` + // This field contains the actual public key. + PublicKeyValue string `json:"publicKeyValue,omitempty"` + // This field contains the corresponding signature scheme. Eg: \"rsassa-pss-sha256\". + KeyScheme string `json:"keyScheme,omitempty"` +} diff --git a/image-scanner/grafeas/model_layer_directive.go b/image-scanner/grafeas/model_layer_directive.go new file mode 100644 index 000000000..698ec8b6e --- /dev/null +++ b/image-scanner/grafeas/model_layer_directive.go @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas +// LayerDirective : Instructions from Dockerfile. - DIRECTIVE_UNSPECIFIED: Default value for unsupported/missing directive. - MAINTAINER: https://docs.docker.com/engine/reference/builder/ - RUN: https://docs.docker.com/engine/reference/builder/ - CMD: https://docs.docker.com/engine/reference/builder/ - LABEL: https://docs.docker.com/engine/reference/builder/ - EXPOSE: https://docs.docker.com/engine/reference/builder/ - ENV: https://docs.docker.com/engine/reference/builder/ - ADD: https://docs.docker.com/engine/reference/builder/ - COPY: https://docs.docker.com/engine/reference/builder/ - ENTRYPOINT: https://docs.docker.com/engine/reference/builder/ - VOLUME: https://docs.docker.com/engine/reference/builder/ - USER: https://docs.docker.com/engine/reference/builder/ - WORKDIR: https://docs.docker.com/engine/reference/builder/ - ARG: https://docs.docker.com/engine/reference/builder/ - ONBUILD: https://docs.docker.com/engine/reference/builder/ - STOPSIGNAL: https://docs.docker.com/engine/reference/builder/ - HEALTHCHECK: https://docs.docker.com/engine/reference/builder/ - SHELL: https://docs.docker.com/engine/reference/builder/ +type LayerDirective string + +// List of LayerDirective +const ( + DIRECTIVE_UNSPECIFIED_LayerDirective LayerDirective = "DIRECTIVE_UNSPECIFIED" + MAINTAINER_LayerDirective LayerDirective = "MAINTAINER" + RUN_LayerDirective LayerDirective = "RUN" + CMD_LayerDirective LayerDirective = "CMD" + LABEL_LayerDirective LayerDirective = "LABEL" + EXPOSE_LayerDirective LayerDirective = "EXPOSE" + ENV_LayerDirective LayerDirective = "ENV" + ADD_LayerDirective LayerDirective = "ADD" + COPY_LayerDirective LayerDirective = "COPY" + ENTRYPOINT_LayerDirective LayerDirective = "ENTRYPOINT" + VOLUME_LayerDirective LayerDirective = "VOLUME" + USER_LayerDirective LayerDirective = "USER" + WORKDIR_LayerDirective LayerDirective = "WORKDIR" + ARG_LayerDirective LayerDirective = "ARG" + ONBUILD_LayerDirective LayerDirective = "ONBUILD" + STOPSIGNAL_LayerDirective LayerDirective = "STOPSIGNAL" + HEALTHCHECK_LayerDirective LayerDirective = "HEALTHCHECK" + SHELL_LayerDirective LayerDirective = "SHELL" +) diff --git a/image-scanner/grafeas/model_link_artifact_hashes.go b/image-scanner/grafeas/model_link_artifact_hashes.go new file mode 100644 index 000000000..f4d46b98d --- /dev/null +++ b/image-scanner/grafeas/model_link_artifact_hashes.go @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Defines a hash object for use in Materials and Products. +type LinkArtifactHashes struct { + Sha256 string `json:"sha256,omitempty"` +} diff --git a/image-scanner/grafeas/model_link_by_products.go b/image-scanner/grafeas/model_link_by_products.go new file mode 100644 index 000000000..19bc1d5a3 --- /dev/null +++ b/image-scanner/grafeas/model_link_by_products.go @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Defines an object for the byproducts field in in-toto links. The suggested fields are \"stderr\", \"stdout\", and \"return-value\". +type LinkByProducts struct { + CustomValues map[string]string `json:"custom_values,omitempty"` +} diff --git a/image-scanner/grafeas/model_link_environment.go b/image-scanner/grafeas/model_link_environment.go new file mode 100644 index 000000000..3ed39b901 --- /dev/null +++ b/image-scanner/grafeas/model_link_environment.go @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Defines an object for the environment field in in-toto links. The suggested fields are \"variables\", \"filesystem\", and \"workdir\". +type LinkEnvironment struct { + CustomValues map[string]string `json:"custom_values,omitempty"` +} diff --git a/image-scanner/grafeas/model_package_architecture.go b/image-scanner/grafeas/model_package_architecture.go new file mode 100644 index 000000000..3d6807a62 --- /dev/null +++ b/image-scanner/grafeas/model_package_architecture.go @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas +// PackageArchitecture : Instruction set architectures supported by various package managers. - ARCHITECTURE_UNSPECIFIED: Unknown architecture. - X86: X86 architecture. - X64: X64 architecture. +type PackageArchitecture string + +// List of packageArchitecture +const ( + ARCHITECTURE_UNSPECIFIED_PackageArchitecture PackageArchitecture = "ARCHITECTURE_UNSPECIFIED" + X86_PackageArchitecture PackageArchitecture = "X86" + X64_PackageArchitecture PackageArchitecture = "X64" +) diff --git a/image-scanner/grafeas/model_package_distribution.go b/image-scanner/grafeas/model_package_distribution.go new file mode 100644 index 000000000..014279cdb --- /dev/null +++ b/image-scanner/grafeas/model_package_distribution.go @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// This represents a particular channel of distribution for a given package. E.g., Debian's jessie-backports dpkg mirror. +type PackageDistribution struct { + // Required. The cpe_uri in [CPE format](https://cpe.mitre.org/specification/) denoting the package manager version distributing a package. + CpeUri string `json:"cpeUri,omitempty"` + // The CPU architecture for which packages in this distribution channel were built. + Architecture *PackageArchitecture `json:"architecture,omitempty"` + // The latest available version of this package in this distribution channel. + LatestVersion *PackageVersion `json:"latestVersion,omitempty"` + // A freeform string denoting the maintainer of this package. + Maintainer string `json:"maintainer,omitempty"` + // The distribution channel-specific homepage for this package. + Url string `json:"url,omitempty"` + // The distribution channel-specific description of this package. + Description string `json:"description,omitempty"` +} diff --git a/image-scanner/grafeas/model_package_installation.go b/image-scanner/grafeas/model_package_installation.go new file mode 100644 index 000000000..83598519e --- /dev/null +++ b/image-scanner/grafeas/model_package_installation.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// This represents how a particular software package may be installed on a system. +type PackageInstallation struct { + // Output only. The name of the installed package. + Name string `json:"name,omitempty"` + // Required. All of the places within the filesystem versions of this package have been found. + Location []V1beta1packageLocation `json:"location,omitempty"` +} diff --git a/image-scanner/grafeas/model_package_package.go b/image-scanner/grafeas/model_package_package.go new file mode 100644 index 000000000..89f593196 --- /dev/null +++ b/image-scanner/grafeas/model_package_package.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// This represents a particular package that is distributed over various channels. E.g., glibc (aka libc6) is distributed by many, at various versions. +type PackagePackage struct { + // Required. Immutable. The name of the package. + Name string `json:"name,omitempty"` + // The various channels by which a package is distributed. + Distribution []PackageDistribution `json:"distribution,omitempty"` +} diff --git a/image-scanner/grafeas/model_package_version.go b/image-scanner/grafeas/model_package_version.go new file mode 100644 index 000000000..79838f77b --- /dev/null +++ b/image-scanner/grafeas/model_package_version.go @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Version contains structured information about the version of a package. +type PackageVersion struct { + // Used to correct mistakes in the version numbering scheme. + Epoch int32 `json:"epoch,omitempty"` + // Required only when version kind is NORMAL. The main part of the version name. + Name string `json:"name,omitempty"` + // The iteration of the package build from the above version. + Revision string `json:"revision,omitempty"` + // Required. Distinguishes between sentinel MIN/MAX versions and normal versions. + Kind *VersionVersionKind `json:"kind,omitempty"` +} diff --git a/image-scanner/grafeas/model_protobuf_any.go b/image-scanner/grafeas/model_protobuf_any.go new file mode 100644 index 000000000..543ed22c6 --- /dev/null +++ b/image-scanner/grafeas/model_protobuf_any.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// `Any` contains an arbitrary serialized protocol buffer message along with a URL that describes the type of the serialized message. Protobuf library provides support to pack/unpack Any values in the form of utility functions or additional generated methods of the Any type. Example 1: Pack and unpack a message in C++. Foo foo = ...; Any any; any.PackFrom(foo); ... if (any.UnpackTo(&foo)) { ... } Example 2: Pack and unpack a message in Java. Foo foo = ...; Any any = Any.pack(foo); ... if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } Example 3: Pack and unpack a message in Python. foo = Foo(...) any = Any() any.Pack(foo) ... if any.Is(Foo.DESCRIPTOR): any.Unpack(foo) ... Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} any, err := ptypes.MarshalAny(foo) ... foo := &pb.Foo{} if err := ptypes.UnmarshalAny(any, foo); err != nil { ... } The pack methods provided by protobuf library will by default use 'type.googleapis.com/full.type.name' as the type URL and the unpack methods only use the fully qualified type name after the last '/' in the type URL, for example \"foo.bar.com/x/y.z\" will yield type name \"y.z\". JSON ==== The JSON representation of an `Any` value uses the regular representation of the deserialized, embedded message, with an additional field `@type` which contains the type URL. Example: package google.profile; message Person { string first_name = 1; string last_name = 2; } { \"@type\": \"type.googleapis.com/google.profile.Person\", \"firstName\": , \"lastName\": } If the embedded message type is well-known and has a custom JSON representation, that representation will be embedded adding a field `value` which holds the custom JSON in addition to the `@type` field. Example (for message [google.protobuf.Duration][]): { \"@type\": \"type.googleapis.com/google.protobuf.Duration\", \"value\": \"1.212s\" } +type ProtobufAny struct { + // A URL/resource name that uniquely identifies the type of the serialized protocol buffer message. This string must contain at least one \"/\" character. The last segment of the URL's path must represent the fully qualified name of the type (as in `path/google.protobuf.Duration`). The name should be in a canonical form (e.g., leading \".\" is not accepted). In practice, teams usually precompile into the binary all types that they expect it to use in the context of Any. However, for URLs which use the scheme `http`, `https`, or no scheme, one can optionally set up a type server that maps type URLs to message definitions as follows: * If no scheme is provided, `https` is assumed. * An HTTP GET on the URL must yield a [google.protobuf.Type][] value in binary format, or produce an error. * Applications are allowed to cache lookup results based on the URL, or have them precompiled into a binary to avoid any lookup. Therefore, binary compatibility needs to be preserved on changes to types. (Use versioned type names to manage breaking changes.) Note: this functionality is not currently available in the official protobuf release, and it is not used for type URLs beginning with type.googleapis.com. Schemes other than `http`, `https` (or the empty scheme) might be used with implementation specific semantics. + TypeUrl string `json:"type_url,omitempty"` + // Must be a valid serialized protocol buffer of the above specified type. + Value string `json:"value,omitempty"` +} diff --git a/image-scanner/grafeas/model_protobuf_field_mask.go b/image-scanner/grafeas/model_protobuf_field_mask.go new file mode 100644 index 000000000..d794f2588 --- /dev/null +++ b/image-scanner/grafeas/model_protobuf_field_mask.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// paths: \"f.a\" paths: \"f.b.d\" Here `f` represents a field in some root message, `a` and `b` fields in the message found in `f`, and `d` a field found in the message in `f.b`. Field masks are used to specify a subset of fields that should be returned by a get operation or modified by an update operation. Field masks also have a custom JSON encoding (see below). # Field Masks in Projections When used in the context of a projection, a response message or sub-message is filtered by the API to only contain those fields as specified in the mask. For example, if the mask in the previous example is applied to a response message as follows: f { a : 22 b { d : 1 x : 2 } y : 13 } z: 8 The result will not contain specific values for fields x,y and z (their value will be set to the default, and omitted in proto text output): f { a : 22 b { d : 1 } } A repeated field is not allowed except at the last position of a paths string. If a FieldMask object is not present in a get operation, the operation applies to all fields (as if a FieldMask of all fields had been specified). Note that a field mask does not necessarily apply to the top-level response message. In case of a REST get operation, the field mask applies directly to the response, but in case of a REST list operation, the mask instead applies to each individual message in the returned resource list. In case of a REST custom method, other definitions may be used. Where the mask applies will be clearly documented together with its declaration in the API. In any case, the effect on the returned resource/resources is required behavior for APIs. # Field Masks in Update Operations A field mask in update operations specifies which fields of the targeted resource are going to be updated. The API is required to only change the values of the fields as specified in the mask and leave the others untouched. If a resource is passed in to describe the updated values, the API ignores the values of all fields not covered by the mask. If a repeated field is specified for an update operation, new values will be appended to the existing repeated field in the target resource. Note that a repeated field is only allowed in the last position of a `paths` string. If a sub-message is specified in the last position of the field mask for an update operation, then new value will be merged into the existing sub-message in the target resource. For example, given the target message: f { b { d: 1 x: 2 } c: [1] } And an update message: f { b { d: 10 } c: [2] } then if the field mask is: paths: [\"f.b\", \"f.c\"] then the result will be: f { b { d: 10 x: 2 } c: [1, 2] } An implementation may provide options to override this default behavior for repeated and message fields. In order to reset a field's value to the default, the field must be in the mask and set to the default value in the provided resource. Hence, in order to reset all fields of a resource, provide a default instance of the resource and set all fields in the mask, or do not provide a mask as described below. If a field mask is not present on update, the operation applies to all fields (as if a field mask of all fields has been specified). Note that in the presence of schema evolution, this may mean that fields the client does not know and has therefore not filled into the request will be reset to their default. If this is unwanted behavior, a specific service may require a client to always specify a field mask, producing an error if not. As with get operations, the location of the resource which describes the updated values in the request message depends on the operation kind. In any case, the effect of the field mask is required to be honored by the API. ## Considerations for HTTP REST The HTTP kind of an update operation which uses a field mask must be set to PATCH instead of PUT in order to satisfy HTTP semantics (PUT must only be used for full updates). # JSON Encoding of Field Masks In JSON, a field mask is encoded as a single string where paths are separated by a comma. Fields name in each path are converted to/from lower-camel naming conventions. As an example, consider the following message declarations: message Profile { User user = 1; Photo photo = 2; } message User { string display_name = 1; string address = 2; } In proto a field mask for `Profile` may look as such: mask { paths: \"user.display_name\" paths: \"photo\" } In JSON, the same mask is represented as below: { mask: \"user.displayName,photo\" } # Field Masks and Oneof Fields Field masks treat fields in oneofs just as regular fields. Consider the following message: message SampleMessage { oneof test_oneof { string name = 4; SubMessage sub_message = 9; } } The field mask can be: mask { paths: \"name\" } Or: mask { paths: \"sub_message\" } Note that oneof type names (\"test_oneof\" in this case) cannot be used in paths. ## Field Mask Verification The implementation of any API method which has a FieldMask type field in the request should verify the included field paths, and return an `INVALID_ARGUMENT` error if any path is duplicated or unmappable. +type ProtobufFieldMask struct { + // The set of field mask paths. + Paths []string `json:"paths,omitempty"` +} diff --git a/image-scanner/grafeas/model_provenance_build_provenance.go b/image-scanner/grafeas/model_provenance_build_provenance.go new file mode 100644 index 000000000..ea78a0e42 --- /dev/null +++ b/image-scanner/grafeas/model_provenance_build_provenance.go @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +import ( + "time" +) + +// Provenance of a build. Contains all information needed to verify the full details about the build from source to completion. +type ProvenanceBuildProvenance struct { + // Required. Unique identifier of the build. + Id string `json:"id,omitempty"` + // ID of the project. + ProjectId string `json:"project_id,omitempty"` + // Commands requested by the build. + Commands []ProvenanceCommand `json:"commands,omitempty"` + // Output of the build. + BuiltArtifacts []V1beta1provenanceArtifact `json:"built_artifacts,omitempty"` + // Time at which the build was created. + CreateTime time.Time `json:"create_time,omitempty"` + // Time at which execution of the build was started. + StartTime time.Time `json:"start_time,omitempty"` + // Time at which execution of the build was finished. + EndTime time.Time `json:"end_time,omitempty"` + // E-mail address of the user who initiated this build. Note that this was the user's e-mail address at the time the build was initiated; this address may not represent the same end-user for all time. + Creator string `json:"creator,omitempty"` + // URI where any logs for this provenance were written. + LogsUri string `json:"logs_uri,omitempty"` + // Details of the Source input to the build. + SourceProvenance *ProvenanceSource `json:"source_provenance,omitempty"` + // Trigger identifier if the build was triggered automatically; empty if not. + TriggerId string `json:"trigger_id,omitempty"` + // Special options applied to this build. This is a catch-all field where build providers can enter any desired additional details. + BuildOptions map[string]string `json:"build_options,omitempty"` + // Version string of the builder at the time this build was executed. + BuilderVersion string `json:"builder_version,omitempty"` +} diff --git a/image-scanner/grafeas/model_provenance_command.go b/image-scanner/grafeas/model_provenance_command.go new file mode 100644 index 000000000..335b7193a --- /dev/null +++ b/image-scanner/grafeas/model_provenance_command.go @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Command describes a step performed as part of the build pipeline. +type ProvenanceCommand struct { + // Required. Name of the command, as presented on the command line, or if the command is packaged as a Docker container, as presented to `docker pull`. + Name string `json:"name,omitempty"` + // Environment variables set before running this command. + Env []string `json:"env,omitempty"` + // Command-line arguments used when executing this command. + Args []string `json:"args,omitempty"` + // Working directory (relative to project source root) used when running this command. + Dir string `json:"dir,omitempty"` + // Optional unique identifier for this command, used in wait_for to reference this command as a dependency. + Id string `json:"id,omitempty"` + // The ID(s) of the command(s) that this command depends on. + WaitFor []string `json:"wait_for,omitempty"` +} diff --git a/image-scanner/grafeas/model_provenance_file_hashes.go b/image-scanner/grafeas/model_provenance_file_hashes.go new file mode 100644 index 000000000..063dd71e2 --- /dev/null +++ b/image-scanner/grafeas/model_provenance_file_hashes.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Container message for hashes of byte content of files, used in source messages to verify integrity of source input to the build. +type ProvenanceFileHashes struct { + // Required. Collection of file hashes. + FileHash []ProvenanceHash `json:"file_hash,omitempty"` +} diff --git a/image-scanner/grafeas/model_provenance_hash.go b/image-scanner/grafeas/model_provenance_hash.go new file mode 100644 index 000000000..73a145556 --- /dev/null +++ b/image-scanner/grafeas/model_provenance_hash.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Container message for hash values. +type ProvenanceHash struct { + // Required. The type of hash that was performed. + Type_ *HashHashType `json:"type,omitempty"` + // Required. The hash value. + Value string `json:"value,omitempty"` +} diff --git a/image-scanner/grafeas/model_provenance_source.go b/image-scanner/grafeas/model_provenance_source.go new file mode 100644 index 000000000..367eeadd7 --- /dev/null +++ b/image-scanner/grafeas/model_provenance_source.go @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Source describes the location of the source used for the build. +type ProvenanceSource struct { + // If provided, the input binary artifacts for the build came from this location. + ArtifactStorageSourceUri string `json:"artifact_storage_source_uri,omitempty"` + // Hash(es) of the build source, which can be used to verify that the original source integrity was maintained in the build. The keys to this map are file paths used as build source and the values contain the hash values for those files. If the build source came in a single package such as a gzipped tarfile (.tar.gz), the FileHash will be for the single path to that file. + FileHashes map[string]ProvenanceFileHashes `json:"file_hashes,omitempty"` + // If provided, the source code used for the build came from this location. + Context *SourceSourceContext `json:"context,omitempty"` + // If provided, some of the source code used for the build may be found in these locations, in the case where the source repository had multiple remotes or submodules. This list will not include the context specified in the context field. + AdditionalContexts []SourceSourceContext `json:"additional_contexts,omitempty"` +} diff --git a/image-scanner/grafeas/model_rpc_status.go b/image-scanner/grafeas/model_rpc_status.go new file mode 100644 index 000000000..ce63ce200 --- /dev/null +++ b/image-scanner/grafeas/model_rpc_status.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// - Simple to use and understand for most users - Flexible enough to meet unexpected needs # Overview The `Status` message contains three pieces of data: error code, error message, and error details. The error code should be an enum value of [google.rpc.Code][google.rpc.Code], but it may accept additional error codes if needed. The error message should be a developer-facing English message that helps developers *understand* and *resolve* the error. If a localized user-facing error message is needed, put the localized message in the error details or localize it in the client. The optional error details may contain arbitrary information about the error. There is a predefined set of error detail types in the package `google.rpc` that can be used for common error conditions. # Language mapping The `Status` message is the logical representation of the error model, but it is not necessarily the actual wire format. When the `Status` message is exposed in different client libraries and different wire protocols, it can be mapped differently. For example, it will likely be mapped to some exceptions in Java, but more likely mapped to some error codes in C. # Other uses The error model and the `Status` message can be used in a variety of environments, either with or without APIs, to provide a consistent developer experience across different environments. Example uses of this error model include: - Partial errors. If a service needs to return partial errors to the client, it may embed the `Status` in the normal response to indicate the partial errors. - Workflow errors. A typical workflow has multiple steps. Each step may have a `Status` message for error reporting. - Batch operations. If a client uses batch request and batch response, the `Status` message should be used directly inside batch response, one for each error sub-response. - Asynchronous operations. If an API call embeds asynchronous operation results in its response, the status of those operations should be represented directly using the `Status` message. - Logging. If some API errors are stored in logs, the message `Status` could be used directly after any stripping needed for security/privacy reasons. +type RpcStatus struct { + // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + Code int32 `json:"code,omitempty"` + // A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + Message string `json:"message,omitempty"` + // A list of messages that carry the error details. There is a common set of message types for APIs to use. + Details []ProtobufAny `json:"details,omitempty"` +} diff --git a/image-scanner/grafeas/model_source_alias_context.go b/image-scanner/grafeas/model_source_alias_context.go new file mode 100644 index 000000000..02faa9562 --- /dev/null +++ b/image-scanner/grafeas/model_source_alias_context.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// An alias to a repo revision. +type SourceAliasContext struct { + // The alias kind. + Kind *AliasContextKind `json:"kind,omitempty"` + // The alias name. + Name string `json:"name,omitempty"` +} diff --git a/image-scanner/grafeas/model_source_cloud_repo_source_context.go b/image-scanner/grafeas/model_source_cloud_repo_source_context.go new file mode 100644 index 000000000..5224989c3 --- /dev/null +++ b/image-scanner/grafeas/model_source_cloud_repo_source_context.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// A CloudRepoSourceContext denotes a particular revision in a Google Cloud Source Repo. +type SourceCloudRepoSourceContext struct { + // The ID of the repo. + RepoId *SourceRepoId `json:"repo_id,omitempty"` + // A revision ID. + RevisionId string `json:"revision_id,omitempty"` + // An alias, which may be a branch or tag. + AliasContext *SourceAliasContext `json:"alias_context,omitempty"` +} diff --git a/image-scanner/grafeas/model_source_gerrit_source_context.go b/image-scanner/grafeas/model_source_gerrit_source_context.go new file mode 100644 index 000000000..e86b7986d --- /dev/null +++ b/image-scanner/grafeas/model_source_gerrit_source_context.go @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// A SourceContext referring to a Gerrit project. +type SourceGerritSourceContext struct { + // The URI of a running Gerrit instance. + HostUri string `json:"host_uri,omitempty"` + // The full project name within the host. Projects may be nested, so \"project/subproject\" is a valid project name. The \"repo name\" is the hostURI/project. + GerritProject string `json:"gerrit_project,omitempty"` + // A revision (commit) ID. + RevisionId string `json:"revision_id,omitempty"` + // An alias, which may be a branch or tag. + AliasContext *SourceAliasContext `json:"alias_context,omitempty"` +} diff --git a/image-scanner/grafeas/model_source_git_source_context.go b/image-scanner/grafeas/model_source_git_source_context.go new file mode 100644 index 000000000..a111c150f --- /dev/null +++ b/image-scanner/grafeas/model_source_git_source_context.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// A GitSourceContext denotes a particular revision in a third party Git repository (e.g., GitHub). +type SourceGitSourceContext struct { + // Git repository URL. + Url string `json:"url,omitempty"` + // Git commit hash. + RevisionId string `json:"revision_id,omitempty"` +} diff --git a/image-scanner/grafeas/model_source_project_repo_id.go b/image-scanner/grafeas/model_source_project_repo_id.go new file mode 100644 index 000000000..52e1593b1 --- /dev/null +++ b/image-scanner/grafeas/model_source_project_repo_id.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Selects a repo using a Google Cloud Platform project ID (e.g., winged-cargo-31) and a repo name within that project. +type SourceProjectRepoId struct { + // The ID of the project. + ProjectId string `json:"project_id,omitempty"` + // The name of the repo. Leave empty for the default repo. + RepoName string `json:"repo_name,omitempty"` +} diff --git a/image-scanner/grafeas/model_source_repo_id.go b/image-scanner/grafeas/model_source_repo_id.go new file mode 100644 index 000000000..6a0bece88 --- /dev/null +++ b/image-scanner/grafeas/model_source_repo_id.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// A unique identifier for a Cloud Repo. +type SourceRepoId struct { + // A combination of a project ID and a repo name. + ProjectRepoId *SourceProjectRepoId `json:"project_repo_id,omitempty"` + // A server-assigned, globally unique identifier. + Uid string `json:"uid,omitempty"` +} diff --git a/image-scanner/grafeas/model_source_source_context.go b/image-scanner/grafeas/model_source_source_context.go new file mode 100644 index 000000000..cb3980e7a --- /dev/null +++ b/image-scanner/grafeas/model_source_source_context.go @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// A SourceContext is a reference to a tree of files. A SourceContext together with a path point to a unique revision of a single file or directory. +type SourceSourceContext struct { + // A SourceContext referring to a revision in a Google Cloud Source Repo. + CloudRepo *SourceCloudRepoSourceContext `json:"cloud_repo,omitempty"` + // A SourceContext referring to a Gerrit project. + Gerrit *SourceGerritSourceContext `json:"gerrit,omitempty"` + // A SourceContext referring to any third party Git repo (e.g., GitHub). + Git *SourceGitSourceContext `json:"git,omitempty"` + // Labels with user defined metadata. + Labels map[string]string `json:"labels,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1_batch_create_notes_request.go b/image-scanner/grafeas/model_v1beta1_batch_create_notes_request.go new file mode 100644 index 000000000..4fdc0c6d5 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1_batch_create_notes_request.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Request to create notes in batch. +type V1beta1BatchCreateNotesRequest struct { + // The name of the project in the form of `projects/[PROJECT_ID]`, under which the notes are to be created. + Parent string `json:"parent,omitempty"` + // The notes to create. Max allowed length is 1000. + Notes map[string]V1beta1Note `json:"notes,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1_batch_create_notes_response.go b/image-scanner/grafeas/model_v1beta1_batch_create_notes_response.go new file mode 100644 index 000000000..9d9143258 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1_batch_create_notes_response.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Response for creating notes in batch. +type V1beta1BatchCreateNotesResponse struct { + // The notes that were created. + Notes []V1beta1Note `json:"notes,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1_batch_create_occurrences_request.go b/image-scanner/grafeas/model_v1beta1_batch_create_occurrences_request.go new file mode 100644 index 000000000..b542961dd --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1_batch_create_occurrences_request.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Request to create occurrences in batch. +type V1beta1BatchCreateOccurrencesRequest struct { + // The name of the project in the form of `projects/[PROJECT_ID]`, under which the occurrences are to be created. + Parent string `json:"parent,omitempty"` + // The occurrences to create. Max allowed length is 1000. + Occurrences []V1beta1Occurrence `json:"occurrences,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1_batch_create_occurrences_response.go b/image-scanner/grafeas/model_v1beta1_batch_create_occurrences_response.go new file mode 100644 index 000000000..5825550c5 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1_batch_create_occurrences_response.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Response for creating occurrences in batch. +type V1beta1BatchCreateOccurrencesResponse struct { + // The occurrences that were created. + Occurrences []V1beta1Occurrence `json:"occurrences,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1_list_note_occurrences_response.go b/image-scanner/grafeas/model_v1beta1_list_note_occurrences_response.go new file mode 100644 index 000000000..9d5b1c6f3 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1_list_note_occurrences_response.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Response for listing occurrences for a note. +type V1beta1ListNoteOccurrencesResponse struct { + // The occurrences attached to the specified note. + Occurrences []V1beta1Occurrence `json:"occurrences,omitempty"` + // Token to provide to skip to a particular spot in the list. + NextPageToken string `json:"next_page_token,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1_list_notes_response.go b/image-scanner/grafeas/model_v1beta1_list_notes_response.go new file mode 100644 index 000000000..6012c9bb7 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1_list_notes_response.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Response for listing notes. +type V1beta1ListNotesResponse struct { + // The notes requested. + Notes []V1beta1Note `json:"notes,omitempty"` + // The next pagination token in the list response. It should be used as `page_token` for the following request. An empty value means no more results. + NextPageToken string `json:"next_page_token,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1_list_occurrences_response.go b/image-scanner/grafeas/model_v1beta1_list_occurrences_response.go new file mode 100644 index 000000000..bd61c0b01 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1_list_occurrences_response.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Response for listing occurrences. +type V1beta1ListOccurrencesResponse struct { + // The occurrences requested. + Occurrences []V1beta1Occurrence `json:"occurrences,omitempty"` + // The next pagination token in the list response. It should be used as `page_token` for the following request. An empty value means no more results. + NextPageToken string `json:"next_page_token,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1_note.go b/image-scanner/grafeas/model_v1beta1_note.go new file mode 100644 index 000000000..be31b946f --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1_note.go @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +import ( + "time" +) + +// A type of analysis that can be done for a resource. +type V1beta1Note struct { + // Output only. The name of the note in the form of `projects/[PROVIDER_ID]/notes/[NOTE_ID]`. + Name string `json:"name,omitempty"` + // A one sentence description of this note. + ShortDescription string `json:"shortDescription,omitempty"` + // A detailed description of this note. + LongDescription string `json:"longDescription,omitempty"` + // Output only. The type of analysis. This field can be used as a filter in list requests. + Kind *V1beta1NoteKind `json:"kind,omitempty"` + // URLs associated with this note. + RelatedUrl []V1beta1RelatedUrl `json:"relatedUrl,omitempty"` + // Time of expiration for this note. Empty if note does not expire. + ExpirationTime time.Time `json:"expirationTime,omitempty"` + // Output only. The time this note was created. This field can be used as a filter in list requests. + CreateTime time.Time `json:"createTime,omitempty"` + // Output only. The time this note was last updated. This field can be used as a filter in list requests. + UpdateTime time.Time `json:"updateTime,omitempty"` + // Other notes related to this note. + RelatedNoteNames []string `json:"relatedNoteNames,omitempty"` + // A note describing a package vulnerability. + Vulnerability *VulnerabilityVulnerability `json:"vulnerability,omitempty"` + // A note describing build provenance for a verifiable build. + Build *BuildBuild `json:"build,omitempty"` + // A note describing a base image. + BaseImage *ImageBasis `json:"baseImage,omitempty"` + // A note describing a package hosted by various package managers. + Package_ *PackagePackage `json:"package,omitempty"` + // A note describing something that can be deployed. + Deployable *DeploymentDeployable `json:"deployable,omitempty"` + // A note describing the initial analysis of a resource. + Discovery *DiscoveryDiscovery `json:"discovery,omitempty"` + // A note describing an attestation role. + AttestationAuthority *AttestationAuthority `json:"attestationAuthority,omitempty"` + // A note describing an in-toto link. + Intoto *IntotoInToto `json:"intoto,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1_note_kind.go b/image-scanner/grafeas/model_v1beta1_note_kind.go new file mode 100644 index 000000000..75e1cb3d0 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1_note_kind.go @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas +// V1beta1NoteKind : Kind represents the kinds of notes supported. - NOTE_KIND_UNSPECIFIED: Unknown. - VULNERABILITY: The note and occurrence represent a package vulnerability. - BUILD: The note and occurrence assert build provenance. - IMAGE: This represents an image basis relationship. - PACKAGE: This represents a package installed via a package manager. - DEPLOYMENT: The note and occurrence track deployment events. - DISCOVERY: The note and occurrence track the initial discovery status of a resource. - ATTESTATION: This represents a logical \"role\" that can attest to artifacts. - INTOTO: This represents an in-toto link. +type V1beta1NoteKind string + +// List of v1beta1NoteKind +const ( + NOTE_KIND_UNSPECIFIED_V1beta1NoteKind V1beta1NoteKind = "NOTE_KIND_UNSPECIFIED" + VULNERABILITY_V1beta1NoteKind V1beta1NoteKind = "VULNERABILITY" + BUILD_V1beta1NoteKind V1beta1NoteKind = "BUILD" + IMAGE_V1beta1NoteKind V1beta1NoteKind = "IMAGE" + PACKAGE__V1beta1NoteKind V1beta1NoteKind = "PACKAGE" + DEPLOYMENT_V1beta1NoteKind V1beta1NoteKind = "DEPLOYMENT" + DISCOVERY_V1beta1NoteKind V1beta1NoteKind = "DISCOVERY" + ATTESTATION_V1beta1NoteKind V1beta1NoteKind = "ATTESTATION" + INTOTO_V1beta1NoteKind V1beta1NoteKind = "INTOTO" +) diff --git a/image-scanner/grafeas/model_v1beta1_occurrence.go b/image-scanner/grafeas/model_v1beta1_occurrence.go new file mode 100644 index 000000000..ee3a2a116 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1_occurrence.go @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +import ( + "time" +) + +// An instance of an analysis type that has been found on a resource. +type V1beta1Occurrence struct { + // Output only. The name of the occurrence in the form of `projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]`. + Name string `json:"name,omitempty"` + // Required. Immutable. The resource for which the occurrence applies. + Resource *V1beta1Resource `json:"resource,omitempty"` + // Required. Immutable. The analysis note associated with this occurrence, in the form of `projects/[PROVIDER_ID]/notes/[NOTE_ID]`. This field can be used as a filter in list requests. + NoteName string `json:"note_name,omitempty"` + // Output only. This explicitly denotes which of the occurrence details are specified. This field can be used as a filter in list requests. + Kind *V1beta1NoteKind `json:"kind,omitempty"` + // A description of actions that can be taken to remedy the note. + Remediation string `json:"remediation,omitempty"` + // Output only. The time this occurrence was created. + CreateTime time.Time `json:"create_time,omitempty"` + // Output only. The time this occurrence was last updated. + UpdateTime time.Time `json:"update_time,omitempty"` + // Describes a security vulnerability. + Vulnerability *V1beta1vulnerabilityDetails `json:"vulnerability,omitempty"` + // Describes a verifiable build. + Build *V1beta1buildDetails `json:"build,omitempty"` + // Describes how this resource derives from the basis in the associated note. + DerivedImage *V1beta1imageDetails `json:"derived_image,omitempty"` + // Describes the installation of a package on the linked resource. + Installation *V1beta1packageDetails `json:"installation,omitempty"` + // Describes the deployment of an artifact on a runtime. + Deployment *V1beta1deploymentDetails `json:"deployment,omitempty"` + // Describes when a resource was discovered. + Discovered *V1beta1discoveryDetails `json:"discovered,omitempty"` + // Describes an attestation of an artifact. + Attestation *V1beta1attestationDetails `json:"attestation,omitempty"` + // Describes a specific in-toto link. + Intoto *V1beta1intotoDetails `json:"intoto,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1_related_url.go b/image-scanner/grafeas/model_v1beta1_related_url.go new file mode 100644 index 000000000..30ad243f5 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1_related_url.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Metadata for any related URL information. +type V1beta1RelatedUrl struct { + // Specific URL associated with the resource. + Url string `json:"url,omitempty"` + // Label to describe usage of the URL. + Label string `json:"label,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1_resource.go b/image-scanner/grafeas/model_v1beta1_resource.go new file mode 100644 index 000000000..b84d7c7f0 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1_resource.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// An entity that can have metadata. For example, a Docker image. +type V1beta1Resource struct { + // Deprecated, do not use. Use uri instead. The name of the resource. For example, the name of a Docker image - \"Debian\". + Name string `json:"name,omitempty"` + // Required. The unique URI of the resource. For example, `https://gcr.io/project/image@sha256:foo` for a Docker image. + Uri string `json:"uri,omitempty"` + // Deprecated, do not use. Use uri instead. The hash of the resource content. For example, the Docker digest. + ContentHash *ProvenanceHash `json:"content_hash,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1_vulnerability_occurrences_summary.go b/image-scanner/grafeas/model_v1beta1_vulnerability_occurrences_summary.go new file mode 100644 index 000000000..7877540fe --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1_vulnerability_occurrences_summary.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// A summary of how many vulnerability occurrences there are per resource and severity type. +type V1beta1VulnerabilityOccurrencesSummary struct { + // A listing by resource of the number of fixable and total vulnerabilities. + Counts []VulnerabilityOccurrencesSummaryFixableTotalByDigest `json:"counts,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1attestation_details.go b/image-scanner/grafeas/model_v1beta1attestation_details.go new file mode 100644 index 000000000..de0aab1c0 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1attestation_details.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Details of an attestation occurrence. +type V1beta1attestationDetails struct { + // Required. Attestation for the resource. + Attestation *AttestationAttestation `json:"attestation,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1build_details.go b/image-scanner/grafeas/model_v1beta1build_details.go new file mode 100644 index 000000000..d8a73d20b --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1build_details.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Details of a build occurrence. +type V1beta1buildDetails struct { + // Required. The actual provenance for the build. + Provenance *ProvenanceBuildProvenance `json:"provenance,omitempty"` + // Serialized JSON representation of the provenance, used in generating the build signature in the corresponding build note. After verifying the signature, `provenance_bytes` can be unmarshalled and compared to the provenance to confirm that it is unchanged. A base64-encoded string representation of the provenance bytes is used for the signature in order to interoperate with openssl which expects this format for signature verification. The serialized form is captured both to avoid ambiguity in how the provenance is marshalled to json as well to prevent incompatibilities with future changes. + ProvenanceBytes string `json:"provenance_bytes,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1deployment_details.go b/image-scanner/grafeas/model_v1beta1deployment_details.go new file mode 100644 index 000000000..f64a4b051 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1deployment_details.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Details of a deployment occurrence. +type V1beta1deploymentDetails struct { + // Required. Deployment history for the resource. + Deployment *DeploymentDeployment `json:"deployment,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1discovery_details.go b/image-scanner/grafeas/model_v1beta1discovery_details.go new file mode 100644 index 000000000..128c238e1 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1discovery_details.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Details of a discovery occurrence. +type V1beta1discoveryDetails struct { + // Required. Analysis status for the discovered resource. + Discovered *DiscoveryDiscovered `json:"discovered,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1image_details.go b/image-scanner/grafeas/model_v1beta1image_details.go new file mode 100644 index 000000000..bc8e42e1d --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1image_details.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Details of an image occurrence. +type V1beta1imageDetails struct { + // Required. Immutable. The child image derived from the base image. + DerivedImage *ImageDerived `json:"derived_image,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1intoto_details.go b/image-scanner/grafeas/model_v1beta1intoto_details.go new file mode 100644 index 000000000..eb7f173dc --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1intoto_details.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// This corresponds to a signed in-toto link - it is made up of one or more signatures and the in-toto link itself. This is used for occurrences of a Grafeas in-toto note. +type V1beta1intotoDetails struct { + Signatures []V1beta1intotoSignature `json:"signatures,omitempty"` + Link *IntotoLink `json:"link,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1intoto_signature.go b/image-scanner/grafeas/model_v1beta1intoto_signature.go new file mode 100644 index 000000000..254c426a0 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1intoto_signature.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// A signature object consists of the KeyID used and the signature itself. +type V1beta1intotoSignature struct { + KeyId string `json:"key_id,omitempty"` + Signature string `json:"signature,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1package_details.go b/image-scanner/grafeas/model_v1beta1package_details.go new file mode 100644 index 000000000..b96a03db6 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1package_details.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Details of a package occurrence. +type V1beta1packageDetails struct { + // Required. Where the package was installed. + Installation *PackageInstallation `json:"installation,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1package_location.go b/image-scanner/grafeas/model_v1beta1package_location.go new file mode 100644 index 000000000..caa89174a --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1package_location.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// An occurrence of a particular package installation found within a system's filesystem. E.g., glibc was found in `/var/lib/dpkg/status`. +type V1beta1packageLocation struct { + // Required. The CPE URI in [CPE format](https://cpe.mitre.org/specification/) denoting the package manager version distributing a package. + CpeUri string `json:"cpe_uri,omitempty"` + // The version installed at this location. + Version *PackageVersion `json:"version,omitempty"` + // The path from which we gathered that this package/version is installed. + Path string `json:"path,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1provenance_artifact.go b/image-scanner/grafeas/model_v1beta1provenance_artifact.go new file mode 100644 index 000000000..ac837e9d3 --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1provenance_artifact.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Artifact describes a build product. +type V1beta1provenanceArtifact struct { + // Hash or checksum value of a binary, or Docker Registry 2.0 digest of a container. + Checksum string `json:"checksum,omitempty"` + // Artifact ID, if any; for container images, this will be a URL by digest like `gcr.io/projectID/imagename@sha256:123456`. + Id string `json:"id,omitempty"` + // Related artifact names. This may be the path to a binary or jar file, or in the case of a container build, the name used to push the container image to Google Container Registry, as presented to `docker push`. Note that a single Artifact ID can have multiple names, for example if two tags are applied to one image. + Names []string `json:"names,omitempty"` +} diff --git a/image-scanner/grafeas/model_v1beta1vulnerability_details.go b/image-scanner/grafeas/model_v1beta1vulnerability_details.go new file mode 100644 index 000000000..bd3bde2de --- /dev/null +++ b/image-scanner/grafeas/model_v1beta1vulnerability_details.go @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Details of a vulnerability Occurrence. +type V1beta1vulnerabilityDetails struct { + Type_ string `json:"type,omitempty"` + // Output only. The note provider assigned Severity of the vulnerability. + Severity *VulnerabilitySeverity `json:"severity,omitempty"` + // Output only. The CVSS score of this vulnerability. CVSS score is on a scale of 0-10 where 0 indicates low severity and 10 indicates high severity. + CvssScore float32 `json:"cvss_score,omitempty"` + // Required. The set of affected locations and their fixes (if available) within the associated resource. + PackageIssue []VulnerabilityPackageIssue `json:"package_issue,omitempty"` + // Output only. A one sentence description of this vulnerability. + ShortDescription string `json:"short_description,omitempty"` + // Output only. A detailed description of this vulnerability. + LongDescription string `json:"long_description,omitempty"` + // Output only. URLs related to this vulnerability. + RelatedUrls []V1beta1RelatedUrl `json:"related_urls,omitempty"` + // The distro assigned severity for this vulnerability when it is available, and note provider assigned severity when distro has not yet assigned a severity for this vulnerability. + EffectiveSeverity *VulnerabilitySeverity `json:"effective_severity,omitempty"` +} diff --git a/image-scanner/grafeas/model_version_version_kind.go b/image-scanner/grafeas/model_version_version_kind.go new file mode 100644 index 000000000..3493e2887 --- /dev/null +++ b/image-scanner/grafeas/model_version_version_kind.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas +// VersionVersionKind : Whether this is an ordinary package version or a sentinel MIN/MAX version. - VERSION_KIND_UNSPECIFIED: Unknown. - NORMAL: A standard package version. - MINIMUM: A special version representing negative infinity. - MAXIMUM: A special version representing positive infinity. +type VersionVersionKind string + +// List of VersionVersionKind +const ( + VERSION_KIND_UNSPECIFIED_VersionVersionKind VersionVersionKind = "VERSION_KIND_UNSPECIFIED" + NORMAL_VersionVersionKind VersionVersionKind = "NORMAL" + MINIMUM_VersionVersionKind VersionVersionKind = "MINIMUM" + MAXIMUM_VersionVersionKind VersionVersionKind = "MAXIMUM" +) diff --git a/image-scanner/grafeas/model_vulnerability_cvs_sv3.go b/image-scanner/grafeas/model_vulnerability_cvs_sv3.go new file mode 100644 index 000000000..fd712ee04 --- /dev/null +++ b/image-scanner/grafeas/model_vulnerability_cvs_sv3.go @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +type VulnerabilityCvsSv3 struct { + // The base score is a function of the base metric scores. + BaseScore float32 `json:"baseScore,omitempty"` + ExploitabilityScore float32 `json:"exploitabilityScore,omitempty"` + ImpactScore float32 `json:"impactScore,omitempty"` + // Base Metrics Represents the intrinsic characteristics of a vulnerability that are constant over time and across user environments. + AttackVector *CvsSv3AttackVector `json:"attack_vector,omitempty"` + AttackComplexity *CvsSv3AttackComplexity `json:"attackComplexity,omitempty"` + PrivilegesRequired *CvsSv3PrivilegesRequired `json:"privilegesRequired,omitempty"` + UserInteraction *CvsSv3UserInteraction `json:"userInteraction,omitempty"` + Scope *CvsSv3Scope `json:"scope,omitempty"` + ConfidentialityImpact *CvsSv3Impact `json:"confidentialityImpact,omitempty"` + IntegrityImpact *CvsSv3Impact `json:"integrityImpact,omitempty"` + AvailabilityImpact *CvsSv3Impact `json:"availabilityImpact,omitempty"` +} diff --git a/image-scanner/grafeas/model_vulnerability_detail.go b/image-scanner/grafeas/model_vulnerability_detail.go new file mode 100644 index 000000000..c2fa74021 --- /dev/null +++ b/image-scanner/grafeas/model_vulnerability_detail.go @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +import ( + "time" +) + +type VulnerabilityDetail struct { + // Required. The CPE URI in [cpe format](https://cpe.mitre.org/specification/) in which the vulnerability manifests. Examples include distro or storage location for vulnerable jar. + CpeUri string `json:"cpeUri,omitempty"` + // Required. The name of the package where the vulnerability was found. + Package_ string `json:"package,omitempty"` + // The min version of the package in which the vulnerability exists. + MinAffectedVersion *PackageVersion `json:"minAffectedVersion,omitempty"` + // The max version of the package in which the vulnerability exists. + MaxAffectedVersion *PackageVersion `json:"maxAffectedVersion,omitempty"` + // The severity (eg: distro assigned severity) for this vulnerability. + SeverityName string `json:"severityNname,omitempty"` + // A vendor-specific description of this note. + Description string `json:"description,omitempty"` + // The fix for this specific package version. + FixedLocation *VulnerabilityVulnerabilityLocation `json:"fixed_location,omitempty"` + // The type of package; whether native or non native(ruby gems, node.js packages etc). + PackageType string `json:"packageType,omitempty"` + // Whether this detail is obsolete. Occurrences are expected not to point to obsolete details. + IsObsolete bool `json:"isObsolete,omitempty"` + // The time this information was last changed at the source. This is an upstream timestamp from the underlying information source - e.g. Ubuntu security tracker. + SourceUpdateTime time.Time `json:"sourceUpdateTime,omitempty"` +} diff --git a/image-scanner/grafeas/model_vulnerability_occurrences_summary_fixable_total_by_digest.go b/image-scanner/grafeas/model_vulnerability_occurrences_summary_fixable_total_by_digest.go new file mode 100644 index 000000000..99aa1eb23 --- /dev/null +++ b/image-scanner/grafeas/model_vulnerability_occurrences_summary_fixable_total_by_digest.go @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// Per resource and severity counts of fixable and total vulnerabilities. +type VulnerabilityOccurrencesSummaryFixableTotalByDigest struct { + // The affected resource. + Resource *V1beta1Resource `json:"resource,omitempty"` + // The severity for this count. SEVERITY_UNSPECIFIED indicates total across all severities. + Severity *VulnerabilitySeverity `json:"severity,omitempty"` + // The number of fixable vulnerabilities associated with this resource. + FixableCount string `json:"fixable_count,omitempty"` + // The total number of vulnerabilities associated with this resource. + TotalCount string `json:"total_count,omitempty"` +} diff --git a/image-scanner/grafeas/model_vulnerability_package_issue.go b/image-scanner/grafeas/model_vulnerability_package_issue.go new file mode 100644 index 000000000..66463f7ee --- /dev/null +++ b/image-scanner/grafeas/model_vulnerability_package_issue.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// This message wraps a location affected by a vulnerability and its associated fix (if one is available). +type VulnerabilityPackageIssue struct { + // Required. The location of the vulnerability. + AffectedLocation *VulnerabilityVulnerabilityLocation `json:"affected_location,omitempty"` + // The location of the available fix for vulnerability. + FixedLocation *VulnerabilityVulnerabilityLocation `json:"fixed_location,omitempty"` + // Deprecated, use Details.effective_severity instead The severity (e.g., distro assigned severity) for this vulnerability. + SeverityName string `json:"severity_name,omitempty"` +} diff --git a/image-scanner/grafeas/model_vulnerability_severity.go b/image-scanner/grafeas/model_vulnerability_severity.go new file mode 100644 index 000000000..da729b6c8 --- /dev/null +++ b/image-scanner/grafeas/model_vulnerability_severity.go @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas +// VulnerabilitySeverity : Note provider-assigned severity/impact ranking. - SEVERITY_UNSPECIFIED: Unknown. - MINIMAL: Minimal severity. - LOW: Low severity. - MEDIUM: Medium severity. - HIGH: High severity. - CRITICAL: Critical severity. +type VulnerabilitySeverity string + +// List of vulnerabilitySeverity +const ( + SEVERITY_UNSPECIFIED_VulnerabilitySeverity VulnerabilitySeverity = "SEVERITY_UNSPECIFIED" + MINIMAL_VulnerabilitySeverity VulnerabilitySeverity = "MINIMAL" + LOW_VulnerabilitySeverity VulnerabilitySeverity = "LOW" + MEDIUM_VulnerabilitySeverity VulnerabilitySeverity = "MEDIUM" + HIGH_VulnerabilitySeverity VulnerabilitySeverity = "HIGH" + CRITICAL_VulnerabilitySeverity VulnerabilitySeverity = "CRITICAL" +) diff --git a/image-scanner/grafeas/model_vulnerability_vulnerability.go b/image-scanner/grafeas/model_vulnerability_vulnerability.go new file mode 100644 index 000000000..93dceda95 --- /dev/null +++ b/image-scanner/grafeas/model_vulnerability_vulnerability.go @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +import ( + "time" +) + +// Vulnerability provides metadata about a security vulnerability in a Note. +type VulnerabilityVulnerability struct { + // The CVSS score for this vulnerability. + CvssScore float32 `json:"cvssScore,omitempty"` + // Note provider assigned impact of the vulnerability. + Severity *VulnerabilitySeverity `json:"severity,omitempty"` + // All information about the package to specifically identify this vulnerability. One entry per (version range and cpe_uri) the package vulnerability has manifested in. + Details []VulnerabilityDetail `json:"details,omitempty"` + // The full description of the CVSSv3. + CvssV3 *VulnerabilityCvsSv3 `json:"cvssV3,omitempty"` + // Windows details get their own format because the information format and model don't match a normal detail. Specifically Windows updates are done as patches, thus Windows vulnerabilities really are a missing package, rather than a package being at an incorrect version. + WindowsDetails []VulnerabilityWindowsDetail `json:"windowsDetails,omitempty"` + // The time this information was last changed at the source. This is an upstream timestamp from the underlying information source - e.g. Ubuntu security tracker. + SourceUpdateTime time.Time `json:"sourceUpdateTime,omitempty"` +} diff --git a/image-scanner/grafeas/model_vulnerability_vulnerability_location.go b/image-scanner/grafeas/model_vulnerability_vulnerability_location.go new file mode 100644 index 000000000..8d8d2613b --- /dev/null +++ b/image-scanner/grafeas/model_vulnerability_vulnerability_location.go @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +// The location of the vulnerability. +type VulnerabilityVulnerabilityLocation struct { + // Required. The CPE URI in [cpe format](https://cpe.mitre.org/specification/) format. Examples include distro or storage location for vulnerable jar. + CpeUri string `json:"cpeUri,omitempty"` + // Required. The package being described. + Package_ string `json:"package,omitempty"` + // Required. The version of the package being described. + Version *PackageVersion `json:"version,omitempty"` +} diff --git a/image-scanner/grafeas/model_vulnerability_windows_detail.go b/image-scanner/grafeas/model_vulnerability_windows_detail.go new file mode 100644 index 000000000..82a971be0 --- /dev/null +++ b/image-scanner/grafeas/model_vulnerability_windows_detail.go @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +type VulnerabilityWindowsDetail struct { + // Required. The CPE URI in [cpe format](https://cpe.mitre.org/specification/) in which the vulnerability manifests. Examples include distro or storage location for vulnerable jar. + CpeUri string `json:"cpeUri,omitempty"` + // Required. The name of the vulnerability. + Name string `json:"name,omitempty"` + // The description of the vulnerability. + Description string `json:"description,omitempty"` + // Required. The names of the KBs which have hotfixes to mitigate this vulnerability. Note that there may be multiple hotfixes (and thus multiple KBs) that mitigate a given vulnerability. Currently any listed kb's presence is considered a fix. + FixingKbs []WindowsDetailKnowledgeBase `json:"fixingKbs,omitempty"` +} diff --git a/image-scanner/grafeas/model_windows_detail_knowledge_base.go b/image-scanner/grafeas/model_windows_detail_knowledge_base.go new file mode 100644 index 000000000..2ca7f017e --- /dev/null +++ b/image-scanner/grafeas/model_windows_detail_knowledge_base.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +type WindowsDetailKnowledgeBase struct { + // The KB name (generally of the form KB[0-9]+ i.e. KB123456). + Name string `json:"name,omitempty"` + Url string `json:"url,omitempty"` +} diff --git a/image-scanner/grafeas/response.go b/image-scanner/grafeas/response.go new file mode 100644 index 000000000..5b30154f8 --- /dev/null +++ b/image-scanner/grafeas/response.go @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * grafeas.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: version not set + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package grafeas + +import ( + "net/http" +) + +type APIResponse struct { + *http.Response `json:"-"` + Message string `json:"message,omitempty"` + // Operation is the name of the swagger operation. + Operation string `json:"operation,omitempty"` + // RequestURL is the request URL. This value is always available, even if the + // embedded *http.Response is nil. + RequestURL string `json:"url,omitempty"` + // Method is the HTTP method used for the request. This value is always + // available, even if the embedded *http.Response is nil. + Method string `json:"method,omitempty"` + // Payload holds the contents of the response body (which may be nil or empty). + // This is provided here as the raw response.Body() reader will have already + // been drained. + Payload []byte `json:"-"` +} + +func NewAPIResponse(r *http.Response) *APIResponse { + + response := &APIResponse{Response: r} + return response +} + +func NewAPIResponseWithError(errorMessage string) *APIResponse { + + response := &APIResponse{Message: errorMessage} + return response +} diff --git a/image-scanner/internals/constants/InternalErrorCode.go b/image-scanner/internals/constants/InternalErrorCode.go new file mode 100644 index 000000000..dcb2d045c --- /dev/null +++ b/image-scanner/internals/constants/InternalErrorCode.go @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package constants + +import "fmt" + +/** + Cluster - 1000-1999 + Environment - 2000-2999 + Global Config - 3000-3999 + Pipeline Config - 4000-4999 + Pipeline - 5000-5999 + User - 6000-6999 + Other - 7000-7999 +*/ + +type ErrorCode struct { + Code string + userErrMessage string +} + +func (code ErrorCode) UserMessage(params ...interface{}) string { + return fmt.Sprintf(code.userErrMessage, params) +} + +const ( + //Cluster Errors + ClusterCreateDBFailed string = "1001" + ClusterCreateACDFailed string = "1002" + ClusterDBRollbackFailed string = "1003" + ClusterUpdateDBFailed string = "1004" + ClusterUpdateACDFailed string = "1005" + ClusterCreateBadRequestACD string = "1006" + ClusterUpdateBadRequestACD string = "1007" + //Environment Errors + EnvironmentCreateDBFailed string = "2001" + EnvironmentUpdateDBFailed string = "2002" + EnvironmentUpdateEnvOverrideFailed string = "2003" + //Global Config Errors + DockerRegCreateFailedInDb string = "3001" + DockerRegCreateFailedInGocd string = "3002" + DockerRegUpdateFailedInDb string = "3003" + DockerRegUpdateFailedInGocd string = "3004" + GitProviderCreateFailedAlreadyExists string = "3005" + GitProviderCreateFailedInDb string = "3006" + GitProviderUpdateProviderNotExists string = "3007" + GitProviderUpdateFailedInDb string = "3008" + DockerRegDeleteFailedInDb string = "3009" + DockerRegDeleteFailedInGocd string = "3010" + GitProviderUpdateFailedInSync string = "3011" + UserCreateDBFailed string = "6001" + UserCreatePolicyFailed string = "6002" + UserUpdateDBFailed string = "6003" + UserUpdatePolicyFailed string = "6004" + UserNoTokenProvided string = "6005" + UserNotFoundForToken string = "6006" + UserCreateFetchRoleFailed string = "6007" + UserUpdateFetchRoleFailed string = "6008" + + AppDetailResourceTreeNotFound string = "7000" + + CasbinPolicyNotCreated string = "8000" +) + +var AppAlreadyExists = &ErrorCode{"4001", "application %s already exists"} diff --git a/image-scanner/internals/step-lib/util/cli-util/handler.go b/image-scanner/internals/step-lib/util/cli-util/handler.go new file mode 100644 index 000000000..0ad61bb6f --- /dev/null +++ b/image-scanner/internals/step-lib/util/cli-util/handler.go @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cli_util + +import ( + "context" + "github.com/devtron-labs/image-scanner/common" + common_util "github.com/devtron-labs/image-scanner/internals/step-lib/util/common-util" + "io" + "log" + "os" + "os/exec" + "sync" +) + +type CliOutputType string + +const ( + CliOutPutTypeStatic CliOutputType = "STATIC" + CliOutPutTypeStream CliOutputType = "STREAM" +) + +func HandleCliRequest(baseCommand, outputFileName string, ctx context.Context, outputType CliOutputType, args map[string]string, cliCommandEnv []string) (output []byte, err error) { + //converting maps of args and their values to a slice of string for execution + argsSlice := make([]string, 0, len(args)) + for arg, value := range args { + //assuming '-' or '--' is provided by user (if applicable) + argsSlice = append(argsSlice, arg) + if value != "" { + argsSlice = append(argsSlice, value) + } + } + command := exec.CommandContext(ctx, common.SHELL_COMMAND, common.COMMAND_ARGS, baseCommand) + command.Env = append(command.Env, cliCommandEnv...) + if outputType == CliOutPutTypeStream { //TODO: make async in further feature iterations + err = executeStreamCliRequest(command, outputFileName) + } else if outputType == CliOutPutTypeStatic { + err, output = executeStaticCliRequest(command, outputFileName) + } + if err != nil { + log.Println("error in executing cli request", "err", err, "req", command, string(output)) + return output, err + } + return output, nil +} + +func executeStaticCliRequest(command *exec.Cmd, outputFileName string) (error, []byte) { + op, err := command.CombinedOutput() + if err != nil { + log.Println("error in running command", "err", err, "op", string(op)) + return err, op + } + // If output is already stored in file, considering the output from file (file is created by tool over here) + if outputFileName != "" && op != nil { + if _, err := os.Stat(outputFileName); err == nil { + op, err = os.ReadFile(outputFileName) + if err != nil { + log.Println("error in reading output file", "err", err) + return err, nil + } + } else { + err = common_util.WriteFile(outputFileName, op) + if err != nil { + log.Println("error in writing cli static command output to file", "err", err) + return err, nil + } + } + } + return nil, op +} + +func executeStreamCliRequest(command *exec.Cmd, outputFileName string) error { + var stdout []byte + var errOutputWrite error + stdoutIn, _ := command.StdoutPipe() + err := command.Start() + if err != nil { + log.Println("failed to start command execution", "err", err) + return err + } + var wg sync.WaitGroup + wg.Add(1) + go func() { + errOutputWrite = copyAndWriteToOutputFile(stdoutIn, outputFileName) + wg.Done() + }() + + wg.Wait() + err = command.Wait() + if err != nil { + log.Println("got error in waiting for command", "err", err) + return err + } + if errOutputWrite != nil { + log.Println("failed to copy and write stream output to file", "err", errOutputWrite) + return errOutputWrite + } + outStr := string(stdout) + log.Printf("out:%s\n", outStr) + return nil +} + +func copyAndWriteToOutputFile(r io.Reader, outputFileName string) error { + var out []byte + buf := make([]byte, 1024, 1024) + for { + n, err := r.Read(buf) + if n > 0 { + d := buf[:n] + out = append(out, d...) + if outputFileName != "" && out != nil { + errWrite := common_util.WriteFile(outputFileName, out) + if errWrite != nil { + log.Println("error in writing buffer output to file", "err", err) + return errWrite + } + } + } + if err != nil && err != io.EOF { + log.Println("error in reading from buffer", "err", err) + return err + } else { + return nil + } + } +} diff --git a/image-scanner/internals/step-lib/util/common-util/util.go b/image-scanner/internals/step-lib/util/common-util/util.go new file mode 100644 index 000000000..52bc62bb6 --- /dev/null +++ b/image-scanner/internals/step-lib/util/common-util/util.go @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package common_util + +import ( + "bytes" + "encoding/json" + "fmt" + "html/template" + "io/fs" + "log" + "os" +) + +const ( + OutputFilePrefix = "tmp-output" //TODO: should this be csv? + DefaultFileCreatePermission fs.FileMode = 0755 +) + +// CreateFile takes a unique identifier for a file and creates it in the current working directory +// it returns name of the file created along with any error if encountered +func CreateFile(fileIdentifier string) (string, error) { + fileName := fmt.Sprintf("%s-%s", OutputFilePrefix, fileIdentifier) + file, err := os.Create(fileName) + if err != nil { + log.Println("error in creating output file") + return fileName, err + } + err = file.Chmod(DefaultFileCreatePermission) + if err != nil { + log.Println("error updating file permission", "err", err, "file", file.Name(), "desiredFilePermission", DefaultFileCreatePermission) + return fileName, err + } + return fileName, nil +} + +func WriteFile(fileName string, data []byte) (err error) { + err = os.WriteFile(fileName, data, DefaultFileCreatePermission) + if err != nil { + log.Println("error in writing to file", "err", err, "fileName", fileName) + return err + } + return nil +} + +func ReadFile(fileName string) ([]byte, error) { + op, err := os.ReadFile(fileName) + if err != nil { + log.Println("error in reading file", "err", err, "fileName", fileName) + return nil, err + } + return op, nil +} + +func ParseJsonTemplate(inputTemplate string, data []byte) (string, error) { + tmpl := template.Must(template.New("").Funcs(template.FuncMap{ + "add": func(a, b int) int { return a + b }, + "len": func(sliceIf interface{}) int { + if slice, ok := sliceIf.([]any); ok { + return len(slice) + } + return 0 + }, + }).Parse(inputTemplate)) + jsonMap := map[string]interface{}{} + err := json.Unmarshal(data, &jsonMap) + if err != nil { + log.Println("error in unmarshalling", "err", err) + return "", err + } + buf := &bytes.Buffer{} + //this check handles the case when Results key is not found in trivy scan report + if _, ok := jsonMap["Results"]; !ok { + return "[]", nil + } + err = tmpl.Execute(buf, jsonMap) + if err != nil { + log.Println("error in executing template", "err", err) + return "", err + } + return buf.String(), nil +} diff --git a/image-scanner/internals/step-lib/util/http-util/handler.go b/image-scanner/internals/step-lib/util/http-util/handler.go new file mode 100644 index 000000000..8e5196b44 --- /dev/null +++ b/image-scanner/internals/step-lib/util/http-util/handler.go @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package http_util + +import ( + "context" + "fmt" + common_util "github.com/devtron-labs/image-scanner/internals/step-lib/util/common-util" + "io" + "log" + "net/http" + "net/url" + "strings" +) + +const ( + HttpMethodTypeGet = "GET" + HttpMethodTypePost = "POST" + HttpMethodTypePut = "PUT" + HttpMethodTypeDelete = "DELETE" +) + +func HandleHTTPRequest(requestUrl string, methodType string, headers map[string]string, queryParams url.Values, requestBody io.Reader, outputFileName string, ctx context.Context) ([]byte, error) { + client := http.Client{} + parsedUrl, err := url.Parse(requestUrl) + if err != nil { + return nil, err + } + if methodType == HttpMethodTypeGet { + q := parsedUrl.Query() + for k, v := range queryParams { + q.Set(k, strings.Join(v, ",")) + } + parsedUrl.RawQuery = q.Encode() + } + req, err := http.NewRequestWithContext(ctx, methodType, parsedUrl.String(), requestBody) + if err != nil { + return nil, err + } + for k, v := range headers { + req.Header.Set(k, v) + } + res, err := client.Do(req) + if err != nil { + return nil, err + } + if res == nil { + return nil, fmt.Errorf("error in http request - %s, received empty response", parsedUrl.String()) + } + responseData, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return nil, fmt.Errorf("error in http request - %s, resp: %s", parsedUrl, responseData) + } + if outputFileName != "" && responseData != nil { + err = common_util.WriteFile(outputFileName, responseData) + if err != nil { + log.Println("error in writing http output to file", "err", err) + return nil, err + } + } + return responseData, nil +} diff --git a/image-scanner/internals/util/BasicProviders.go b/image-scanner/internals/util/BasicProviders.go new file mode 100644 index 000000000..33cbf80d9 --- /dev/null +++ b/image-scanner/internals/util/BasicProviders.go @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package util + +import ( + "math/rand" + "strings" + "time" + + "go.uber.org/zap" +) + +var ( + // logger is the defaut logger + logger *zap.SugaredLogger + //FIXME: remove this + //defer logger.Sync() +) + +// Deprecated: instead calling this method inject logger from wire +func GetLogger() *zap.SugaredLogger { + return logger +} + +func init() { + l, err := zap.NewProduction() + if err != nil { + panic("failed to create the default logger: " + err.Error()) + } + logger = l.Sugar() +} + +var chars = []rune("abcdefghijklmnopqrstuvwxyz0123456789") + +// Generates random string +func Generate(size int) string { + rand.Seed(time.Now().UnixNano()) + var b strings.Builder + for i := 0; i < size; i++ { + b.WriteRune(chars[rand.Intn(len(chars))]) + } + str := b.String() + return str +} diff --git a/image-scanner/internals/util/ErrorUtil.go b/image-scanner/internals/util/ErrorUtil.go new file mode 100644 index 000000000..0fcbfa005 --- /dev/null +++ b/image-scanner/internals/util/ErrorUtil.go @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package util + +import ( + "fmt" + "github.com/go-pg/pg" +) + +type ApiError struct { + HttpStatusCode int `json:"-"` + Code string `json:"code,omitempty"` + InternalMessage string `json:"internalMessage,omitempty"` + UserMessage interface{} `json:"userMessage,omitempty"` + UserDetailMessage string `json:"userDetailMessage,omitempty"` +} + +func (e *ApiError) Error() string { + return e.InternalMessage +} + +// default internals will be set +func (e *ApiError) ErrorfInternal(format string, a ...interface{}) error { + return &ApiError{InternalMessage: fmt.Sprintf(format, a...)} +} + +// default user message will be set +func (e ApiError) ErrorfUser(format string, a ...interface{}) error { + return &ApiError{InternalMessage: fmt.Sprintf(format, a...)} +} + +func IsErrNoRows(err error) bool { + return pg.ErrNoRows == err +} diff --git a/image-scanner/main.go b/image-scanner/main.go new file mode 100644 index 000000000..9df83a7f2 --- /dev/null +++ b/image-scanner/main.go @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "fmt" + "log" + "os" + "os/signal" + "syscall" +) + +func main() { + fmt.Println("hello") + app, err := InitializeApp() + if err != nil { + log.Panic(err) + } + // gracefulStop start + var gracefulStop = make(chan os.Signal) + signal.Notify(gracefulStop, syscall.SIGTERM) + signal.Notify(gracefulStop, syscall.SIGINT) + go func() { + sig := <-gracefulStop + fmt.Printf("caught term sig: %+v", sig) + app.Stop() + os.Exit(0) + }() + // gracefulStop end + app.Start() + +/* cs := &klarService.KlarServiceImpl{} + + cs.Process(&common.ScanEvent{ + Image: "686244538589.dkr.ecr.us-east-2.amazonaws.com/devtron:88775627-56-731", + ImageDigest: "sha256:49ad28c8b3b7b2485c4baf4d8538fd31cac4d9bd0aa25d120a8cede6d675ad7e", + AppId: 0, + EnvId: 0, + PipelineId: 0, + CiArtifactId: 0, + UserId: 0, + })*/ + +} diff --git a/image-scanner/pkg/clairService/ClairService.go b/image-scanner/pkg/clairService/ClairService.go new file mode 100644 index 000000000..4246f75b4 --- /dev/null +++ b/image-scanner/pkg/clairService/ClairService.go @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package clairService + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "github.com/caarlos0/env/v6" + "github.com/devtron-labs/image-scanner/common" + "github.com/devtron-labs/image-scanner/pkg/roundTripper" + "github.com/devtron-labs/image-scanner/pkg/security" + "github.com/devtron-labs/image-scanner/pkg/sql/repository" + "github.com/go-pg/pg" + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/quay/claircore" + "go.uber.org/zap" + "io/ioutil" + "net/http" + "net/url" + "path" + "strings" +) + +type ClairConfig struct { + ClairAddress string `env:"CLAIR_ADDR" envDefault:"http://localhost:6060"` +} + +const ( + CLAIR_INDEX_REPORT_URL = "/indexer/api/v1/index_report" + CLAIR_VULNERABILITY_REPORT_URL = "/matcher/api/v1/vulnerability_report" +) + +type ClairService interface { + ScanImage(scanEvent *common.ImageScanEvent, tool *repository.ScanToolMetadata, executionHistory *repository.ImageScanExecutionHistory) (*common.ScanEventResponse, error) + CheckIfIndexReportExistsForManifestHash(manifestHash claircore.Digest) (bool, error) + CreateIndexReportFromManifest(manifest *claircore.Manifest) error + GetVulnerabilityReportFromManifestHash(manifestHash claircore.Digest) (*claircore.VulnerabilityReport, error) + DeleteIndexReportFromManifestHash(manifestHash claircore.Digest) error +} +type ClairServiceImpl struct { + Logger *zap.SugaredLogger + ClairConfig *ClairConfig + HttpClient *http.Client + ImageScanService security.ImageScanService + DockerArtifactStoreRepository repository.DockerArtifactStoreRepository + ScanToolMetadataRepository repository.ScanToolMetadataRepository + RoundTripperService roundTripper.RoundTripperService +} + +func NewClairServiceImpl(logger *zap.SugaredLogger, clairConfig *ClairConfig, + httpClient *http.Client, imageScanService security.ImageScanService, + dockerArtifactStoreRepository repository.DockerArtifactStoreRepository, + scanToolMetadataRepository repository.ScanToolMetadataRepository, roundTripperService roundTripper.RoundTripperService) *ClairServiceImpl { + return &ClairServiceImpl{ + Logger: logger, + ClairConfig: clairConfig, + HttpClient: httpClient, + ImageScanService: imageScanService, + DockerArtifactStoreRepository: dockerArtifactStoreRepository, + ScanToolMetadataRepository: scanToolMetadataRepository, + RoundTripperService: roundTripperService, + } +} + +// below code is used from clairctl (changed auth method according to our need) : https://github.com/quay/clair/blob/v4.3.6/cmd/clairctl/client.go#L32 + +const ( + userAgent = `clairctl/1` +) + +// clairctl code ends + +func GetClairConfig() (*ClairConfig, error) { + cfg := &ClairConfig{} + err := env.Parse(cfg) + if err != nil { + return nil, errors.New("could not get clair config from environment") + } + if !strings.HasPrefix(cfg.ClairAddress, "http://") && !strings.HasPrefix(cfg.ClairAddress, "https://") { + cfg.ClairAddress = fmt.Sprintf("http://%s", cfg.ClairAddress) + } + return cfg, err +} + +func (impl *ClairServiceImpl) ScanImage(scanEvent *common.ImageScanEvent, tool *repository.ScanToolMetadata, executionHistory *repository.ImageScanExecutionHistory) (*common.ScanEventResponse, error) { + impl.Logger.Debugw("new request, scan image", "requestPayload", scanEvent) + scanEventResponse := &common.ScanEventResponse{ + RequestData: scanEvent, + } + _, isImageScanned, err := impl.ImageScanService.IsImageScanned(scanEvent.Image, false) + if err != nil && err != pg.ErrNoRows { + impl.Logger.Errorw("error in fetching scan history ", "err", err, "image", scanEvent.Image) + return nil, err + } + if isImageScanned { + impl.Logger.Infow("image already scanned, skipping further process", "image", scanEvent.Image) + return scanEventResponse, nil + } + + vulnerabilityReport, err := impl.GetVulnerabilityReportFromClair(scanEvent) + if err != nil { + impl.Logger.Errorw("error in getting vulnerability report from clair", "err", err, "scanEvent", scanEvent) + return nil, err + } + + var vulnerabilities []*claircore.Vulnerability + for _, vulnerability := range vulnerabilityReport.Vulnerabilities { + vulnerabilities = append(vulnerabilities, vulnerability) + } + _, err = impl.ImageScanService.CreateScanExecutionRegistryForClairV4(vulnerabilities, scanEvent, tool.Id, executionHistory) + if err != nil { + impl.Logger.Errorw("error in CreateScanExecutionRegistry", "err", err) + return scanEventResponse, err + } + scanEventResponse.ResponseDataClairV4 = vulnerabilities + return scanEventResponse, nil +} + +func (impl *ClairServiceImpl) GetVulnerabilityReportFromClair(scanEvent *common.ImageScanEvent) (*claircore.VulnerabilityReport, error) { + //get manifest from image + manifest, err := impl.CreateClairManifest(scanEvent) + if err != nil { + impl.Logger.Errorw("error in creating clair manifest", "err", err, "scanEvent", scanEvent) + return nil, err + } + //end get manifest + + //checking if index report exists for this manifest hash; if it does, no need of creating index report + exists, err := impl.CheckIfIndexReportExistsForManifestHash(manifest.Hash) + if err != nil { + impl.Logger.Infow("err in checking if index report exists, trying once again", "err", err, "manifestHash", manifest.Hash) + exists, err := impl.CheckIfIndexReportExistsForManifestHash(manifest.Hash) + if !exists { + impl.Logger.Infow("could not check if index report exists in second try", "err", err, "manifestHash", manifest.Hash) + err = impl.CreateIndexReportFromManifest(manifest) + if err != nil { + impl.Logger.Errorw("error in creating clair index report", "err", err, "manifest", manifest) + return nil, err + } + } + } else if !exists { + //index report do not exist, creating index report for manifest + err = impl.CreateIndexReportFromManifest(manifest) + if err != nil { + impl.Logger.Errorw("error in creating clair index report", "err", err, "manifest", manifest) + return nil, err + } + } + + //index report created, now getting vulnerability report + vulnerabilityReport, err := impl.GetVulnerabilityReportFromManifestHash(manifest.Hash) + if err != nil { + impl.Logger.Errorw("error in getting vulnerability report by manifest hash", "err", err, "manifestHash", manifest.Hash) + return nil, err + } + + //trying to delete index report for manifest hash, if unsuccessful will log and skip + err = impl.DeleteIndexReportFromManifestHash(manifest.Hash) + if err != nil { + impl.Logger.Warnw("error in deleting index report from manifest hash", "err", err, "manifestHash", manifest.Hash) + } + return vulnerabilityReport, nil +} + +func (impl *ClairServiceImpl) CreateClairManifest(scanEvent *common.ImageScanEvent) (*claircore.Manifest, error) { + roundTripper, err := impl.RoundTripperService.GetRoundTripper(scanEvent) + if err != nil { + impl.Logger.Errorw("error in getting round tripper", "err", "image", scanEvent.Image) + return nil, err + } + reference, err := name.ParseReference(scanEvent.Image) + if err != nil { + impl.Logger.Errorw("error in parsing reference of image", "err", err, "image", scanEvent.Image) + return nil, err + } + descriptor, err := remote.Get(reference, remote.WithTransport(roundTripper)) + if err != nil { + impl.Logger.Errorw("error in getting image descriptor for given reference", "err", err, "reference", reference) + return nil, err + } + image, err := descriptor.Image() + if err != nil { + impl.Logger.Errorw("error in getting image by descriptor", "err", err, "descriptor", descriptor) + return nil, err + } + manifest, err := impl.GenerateClairManifestFromImage(image, reference, roundTripper) + if err != nil { + impl.Logger.Errorw("error in generating clair manifest from image", "err", err, "image", image) + return nil, err + } + return manifest, nil +} + +func (impl *ClairServiceImpl) GenerateClairManifestFromImage(image v1.Image, reference name.Reference, roundTripper http.RoundTripper) (*claircore.Manifest, error) { + imageDigest, err := image.Digest() + if err != nil { + impl.Logger.Errorw("error in getting imageDigest by image", "err", err, "image", image) + return nil, err + } + parsedImageDigest, err := claircore.ParseDigest(imageDigest.String()) + if err != nil { + impl.Logger.Errorw("error in getting parsing imageDigest", "err", err, "imageDigest", imageDigest) + return nil, err + } + impl.Logger.Debugw("got hash for clair manifest", "hash", parsedImageDigest) + manifest := &claircore.Manifest{ + Hash: parsedImageDigest, + } + + layers, err := image.Layers() + if err != nil { + impl.Logger.Errorw("error in getting layers from image", "err", err, "image", image) + return nil, err + } + impl.Logger.Debugw("got image layers", "layers", layers, "image", image) + + //getting repository from reference + repository := reference.Context() + repositoryURL := url.URL{ + Scheme: repository.Scheme(), + Host: repository.RegistryStr(), + } + httpClient := http.Client{ + Transport: roundTripper, + } + for _, layer := range layers { + layerDigest, err := layer.Digest() + if err != nil { + impl.Logger.Errorw("error in getting image digest", "err", err, "image", image) + return nil, err + } + parsedLayerDigest, err := claircore.ParseDigest(layerDigest.String()) + if err != nil { + impl.Logger.Errorw("error in parsing layerDigest", "err", err, "layerDigest", layerDigest) + return nil, err + } + parsedRepositoryUrl, err := repositoryURL.Parse(path.Join("/", "v2", strings.TrimPrefix(repository.RepositoryStr(), repository.RegistryStr()), "blobs", layerDigest.String())) + if err != nil { + impl.Logger.Errorw("error in parsing repositoryUrl", "err", err, "repositoryUrl", repositoryURL) + return nil, err + } + httpRequest, err := http.NewRequest(http.MethodGet, parsedRepositoryUrl.String(), nil) + if err != nil { + impl.Logger.Errorw("error in creating new http request", "err", err, "method", http.MethodGet, "url", parsedRepositoryUrl.String()) + return nil, err + } + httpRequest.Header.Add("Range", "bytes=0-0") + httpResponse, err := httpClient.Do(httpRequest) + if err != nil { + impl.Logger.Errorw("error in sending http request", "err", err, "httpRequest", httpRequest) + return nil, err + } + err = httpResponse.Body.Close() + if err != nil { + impl.Logger.Errorw("error in closing http request", "err", err) + } + httpResponse.Request.Header.Del("User-Agent") + httpResponse.Request.Header.Del("Range") + manifest.Layers = append(manifest.Layers, &claircore.Layer{ + Hash: parsedLayerDigest, + URI: httpResponse.Request.URL.String(), + Headers: httpResponse.Request.Header, + }) + } + return manifest, nil +} +func (impl *ClairServiceImpl) CheckIfIndexReportExistsForManifestHash(manifestHash claircore.Digest) (bool, error) { + impl.Logger.Debugw("new request, check if index report exists for manifest hash", "manifestHash", manifestHash) + + //url - base url + "/indexer/api/v1/index_report/{manifest_hash}" + checkIndexReportUrl, err := url.Parse(impl.ClairConfig.ClairAddress) + if err != nil { + impl.Logger.Errorw("error in parsing clair address url", "err", err, "clairAddress", impl.ClairConfig.ClairAddress) + return false, err + } + checkIndexReportUrl.Path = path.Join(checkIndexReportUrl.Path, CLAIR_INDEX_REPORT_URL, manifestHash.String()) + request, err := http.NewRequest(http.MethodGet, checkIndexReportUrl.String(), nil) + if err != nil { + impl.Logger.Errorw("error in creating new http request", "err", err, "requestUrl", checkIndexReportUrl) + return false, err + } + response, err := impl.HttpClient.Do(request) + if err != nil { + impl.Logger.Errorw("error in http request - CheckIfIndexReportExistsForManifestHash", "err", err, "manifestHash", manifestHash) + return false, err + } + status := response.StatusCode + if !(status >= 200 && status <= 299) { + impl.Logger.Infow("index report does not exists for given manifest hash", "responseStatusCode", response.StatusCode, "manifestHash", manifestHash) + return false, nil + } + impl.Logger.Debugw("received response - index report exists for given manifest hash", "manifestHash", manifestHash) + return true, nil +} + +func (impl *ClairServiceImpl) CreateIndexReportFromManifest(manifest *claircore.Manifest) error { + impl.Logger.Debugw("new request, create index report from manifest", "manifest", manifest) + requestBody, err := json.Marshal(manifest) + if err != nil { + impl.Logger.Errorw("error while marshaling request manifest", "err", err) + return err + } + getIndexReportUrl, err := url.Parse(impl.ClairConfig.ClairAddress) + if err != nil { + impl.Logger.Errorw("error in parsing clair address url", "err", err, "clairAddress", impl.ClairConfig.ClairAddress) + return err + } + getIndexReportUrl.Path = path.Join(getIndexReportUrl.Path, CLAIR_INDEX_REPORT_URL) + request, err := http.NewRequest(http.MethodPost, getIndexReportUrl.String(), bytes.NewBuffer(requestBody)) + if err != nil { + impl.Logger.Errorw("error in creating new http request", "err", err, "requestUrl", getIndexReportUrl, "requestBody", requestBody) + return err + } + request.Header.Set("Content-Type", "application/json") + indexReport, err := impl.HttpClient.Do(request) + if err != nil { + impl.Logger.Errorw("error in http request - CreateIndexReportFromManifest", "err", err, "manifest", manifest) + return err + } + impl.Logger.Debugw("created new index report from manifest", "indexReport", indexReport) + return nil +} + +func (impl *ClairServiceImpl) GetVulnerabilityReportFromManifestHash(manifestHash claircore.Digest) (*claircore.VulnerabilityReport, error) { + impl.Logger.Debugw("new request, get vulnerability report from manifest hash", "manifestHash", manifestHash) + + //url - base url + "/matcher/api/v1/vulnerability_report/{manifest_hash}" + getVulnerabilityReportUrl, err := url.Parse(impl.ClairConfig.ClairAddress) + if err != nil { + impl.Logger.Errorw("error in parsing clair address url", "err", err, "clairAddress", impl.ClairConfig.ClairAddress) + return nil, err + } + getVulnerabilityReportUrl.Path = path.Join(getVulnerabilityReportUrl.Path, CLAIR_VULNERABILITY_REPORT_URL, manifestHash.String()) + request, err := http.NewRequest(http.MethodGet, getVulnerabilityReportUrl.String(), nil) + if err != nil { + impl.Logger.Errorw("error in creating new http request", "err", err, "requestUrl", getVulnerabilityReportUrl) + return nil, err + } + response, err := impl.HttpClient.Do(request) + if err != nil { + impl.Logger.Errorw("error in http request - GetVulnerabilityReportFromManifestHash", "err", err, "manifestHash", manifestHash) + return nil, err + } + + status := response.StatusCode + vulnerabilityReport := &claircore.VulnerabilityReport{} + if status >= 200 && status <= 299 { + responseBody, err := ioutil.ReadAll(response.Body) + if err != nil { + impl.Logger.Errorw("error in reading http response body - GetVulnerabilityReportFromManifestHash", "err", err) + return nil, err + } + err = json.Unmarshal(responseBody, vulnerabilityReport) + if err != nil { + impl.Logger.Errorw("error in un-marshaling vulnerability report", "err", err, "responseBody", responseBody) + return nil, err + } + } else { + impl.Logger.Errorw("http request did not succeed", "response", response) + return nil, fmt.Errorf("http request did not succeed, code: %d", status) + } + + impl.Logger.Debugw("got vulnerability report from manifest hash", "vulnerabilityReport", vulnerabilityReport) + return vulnerabilityReport, nil +} + +func (impl *ClairServiceImpl) DeleteIndexReportFromManifestHash(manifestHash claircore.Digest) error { + impl.Logger.Debugw("new request, delete index report from manifest hash", "manifestHash", manifestHash) + + //url - base url + "/indexer/api/v1/index_report/{manifest_hash}" + deleteIndexReportUrl, err := url.Parse(impl.ClairConfig.ClairAddress) + if err != nil { + impl.Logger.Errorw("error in parsing clair address url", "err", err, "clairAddress", impl.ClairConfig.ClairAddress) + return err + } + deleteIndexReportUrl.Path = path.Join(deleteIndexReportUrl.Path, CLAIR_INDEX_REPORT_URL, manifestHash.String()) + request, err := http.NewRequest(http.MethodDelete, deleteIndexReportUrl.String(), nil) + if err != nil { + impl.Logger.Errorw("error in creating new http request", "err", err, "requestUrl", deleteIndexReportUrl) + return err + } + _, err = impl.HttpClient.Do(request) + if err != nil { + impl.Logger.Errorw("error in http request - DeleteIndexReportFromManifestHash", "err", err, "manifestHash", manifestHash) + return err + } + impl.Logger.Debugw("deleted index report from manifest hash", "manifestHash", manifestHash) + return nil +} diff --git a/image-scanner/pkg/grafeasService/grafeasService.go b/image-scanner/pkg/grafeasService/grafeasService.go new file mode 100644 index 000000000..9044e1020 --- /dev/null +++ b/image-scanner/pkg/grafeasService/grafeasService.go @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package grafeasService + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/devtron-labs/image-scanner/common" + "github.com/devtron-labs/image-scanner/grafeas" + "github.com/optiopay/klar/clair" + "go.uber.org/zap" + "io/ioutil" + "net/http" +) + +type GrafeasConfig struct { + ProjectId string `env:"PROJECT_ID" envDefault:"projects/devtron-project-id"` +} + +func GetGrafeasClient() *grafeas.APIClient { + client := grafeas.NewAPIClient(&grafeas.Configuration{ + BasePath: "http://localhost:8082", + DefaultHeader: make(map[string]string), + }) + return client +} + +type GrafeasService interface { + GetNotesById(noteID string) (*grafeas.V1beta1Note, error) + GetAllNotes() ([]*grafeas.V1beta1Note, error) + CreateNote(vs []*clair.Vulnerability, event *common.ImageScanEvent) (bool, error) + CreateOccurrence(v *clair.Vulnerability, noteName string, event *common.ImageScanEvent) (bool, error) + GetOccurrenceById(noteID string) (*grafeas.V1beta1Occurrence, error) + GetAllOccurrence() ([]*grafeas.V1beta1Occurrence, error) +} + +type GrafeasServiceImpl struct { + logger *zap.SugaredLogger + client *grafeas.APIClient + httpClient *http.Client +} + +func NewKlarServiceImpl(logger *zap.SugaredLogger, client *grafeas.APIClient, httpClient *http.Client) *GrafeasServiceImpl { + return &GrafeasServiceImpl{ + logger: logger, + client: client, + httpClient: httpClient, + } +} + +const basePath string = "http://localhost:8081/v1beta1" +const projectID string = "projects/devtron_test_project" + +func (impl *GrafeasServiceImpl) GetNotesById(noteID string) (*grafeas.V1beta1Note, error) { + noteID = "devtron-note-id" + /* + note := fmt.Sprintf("%s/notes/%s", projectID, noteID) + ctx := context.Background() + client := grafeas.NewAPIClient(&grafeas.Configuration{BasePath: "http://localhost:8081", DefaultHeader: make(map[string]string)}) + resp, httpResponse, err := impl.client.GrafeasV1Beta1Api.GetNote(ctx, note) + if err != nil { + impl.logger.Errorw("Failed to data from grafeas", "err", err) + return err + } + fmt.Println(resp) + fmt.Println(httpResponse) + */ + url := fmt.Sprintf("%s/%s/notes/%s", basePath, projectID, noteID) + httpResponse, err := impl.httpGet(url) + if err != nil { + impl.logger.Errorw("Failed to get from grafeas", "err", err) + return nil, err + } + + defer httpResponse.Body.Close() + var noteResponse grafeas.V1beta1Note + resBody, err := ioutil.ReadAll(httpResponse.Body) + if err != nil { + impl.logger.Errorw("error in parsing response ", "err", err) + return nil, err + } + if httpResponse.StatusCode >= 200 && httpResponse.StatusCode <= 300 { + err = json.Unmarshal(resBody, ¬eResponse) + if err != nil { + impl.logger.Errorw("error in resp Unmarshal ", "err", err) + return nil, err + } + } + impl.logger.Errorw("api response", "status code", httpResponse.StatusCode) + impl.logger.Info(noteResponse) + return ¬eResponse, nil +} + +func (impl *GrafeasServiceImpl) GetAllNotes() ([]*grafeas.V1beta1Note, error) { + + /* + ctx := context.Background() + client := grafeas.NewAPIClient(&grafeas.Configuration{BasePath: "http://localhost:8081"}) + resp, httpResponse, err := impl.client.GrafeasV1Beta1Api.ListNotes(ctx, projectID, nil) + if err != nil { + impl.logger.Errorw("Failed to get from grafeas", "err", err) + return err + } + fmt.Println(resp) + fmt.Println(httpResponse) + */ + url := fmt.Sprintf("%s/%s/notes", basePath, projectID) + httpResponse, err := impl.httpGet(url) + if err != nil { + impl.logger.Errorw("Failed to get from grafeas", "err", err) + return nil, err + } + defer httpResponse.Body.Close() + var noteResponse []*grafeas.V1beta1Note + resBody, err := ioutil.ReadAll(httpResponse.Body) + if err != nil { + impl.logger.Errorw("error in parsing response ", "err", err) + return nil, err + } + if httpResponse.StatusCode >= 200 && httpResponse.StatusCode <= 300 { + err = json.Unmarshal(resBody, ¬eResponse) + if err != nil { + impl.logger.Errorw("error in resp Unmarshal ", "err", err) + return nil, err + } + } + impl.logger.Errorw("api response", "status code", httpResponse.StatusCode) + impl.logger.Info(noteResponse) + return noteResponse, nil +} + +func (impl *GrafeasServiceImpl) CreateNote(vs []*clair.Vulnerability, event *common.ImageScanEvent) (bool, error) { + for _, item := range vs { + var vulnerabilityDetails []grafeas.VulnerabilityDetail + vulnerabilityDetails = append(vulnerabilityDetails, grafeas.VulnerabilityDetail{ + CpeUri: item.NamespaceName, + Package_: item.FeatureName, + }) + //vulnerability name here consider as noteID + noteID := item.Name + kind := grafeas.VULNERABILITY_V1beta1NoteKind + vulnerabilityVulnerability := grafeas.VulnerabilityVulnerability{} + vulnerabilityVulnerability.Details = vulnerabilityDetails + req := grafeas.V1beta1Note{ + Name: fmt.Sprintf("%s/notes/%s", projectID, noteID), + ShortDescription: "A short description of the note", + Kind: &kind, + Vulnerability: &vulnerabilityVulnerability, + } + + /* + ctx := context.Background() + client := grafeas.NewAPIClient(&grafeas.Configuration{BasePath: "http://localhost:8081"}) + resp, httpResponse, err := impl.client.GrafeasV1Beta1Api.CreateNote(ctx, projectID, req) + if err != nil { + impl.logger.Errorw("Failed to post to grafeas", "err", err) + return err + } + */ + + url := fmt.Sprintf("%s/%s/notes?note_id=%s", basePath, projectID, noteID) + b, err := json.Marshal(&req) + if err != nil { + b = []byte("OK") + } + reqBody := []byte(b) + httpResponse, err := impl.httpPost(reqBody, url) + if err != nil { + impl.logger.Errorw("Failed to post to grafeas", "err", err) + return false, err + } + impl.logger.Error(httpResponse) + + _, err = impl.CreateOccurrence(item, req.Name, event) + if err != nil { + impl.logger.Errorw("Failed to post to grafeas", "err", err) + return false, err + } + } + return true, nil +} + +func (impl *GrafeasServiceImpl) CreateOccurrence(v *clair.Vulnerability, noteName string, event *common.ImageScanEvent) (bool, error) { + kind := grafeas.VULNERABILITY_V1beta1NoteKind + versionKind := grafeas.NORMAL_VersionVersionKind + vulnerabilityVulnerability := grafeas.V1beta1vulnerabilityDetails{} + var packageIssues []grafeas.VulnerabilityPackageIssue + packageIssue := grafeas.VulnerabilityPackageIssue{ + AffectedLocation: &grafeas.VulnerabilityVulnerabilityLocation{CpeUri: "devtron package storage", Package_: "devtron package", Version: &grafeas.PackageVersion{Name: "devtron package", Kind: &versionKind}}, + FixedLocation: &grafeas.VulnerabilityVulnerabilityLocation{CpeUri: "devtron package storage", Package_: "devtron package", Version: &grafeas.PackageVersion{Name: "devtron package", Kind: &versionKind}}, + } + packageIssues = append(packageIssues, packageIssue) + vulnerabilityVulnerability.PackageIssue = packageIssues + occurrence := grafeas.V1beta1Occurrence{ + Kind: &kind, + Resource: &grafeas.V1beta1Resource{ + Uri: event.Image, + }, + NoteName: noteName, + Vulnerability: &vulnerabilityVulnerability, + } + /* + ctx := context.Background() + resp, httpResponse, err := impl.client.GrafeasV1Beta1Api.CreateOccurrence(ctx, projectID, occurrence) + if err != nil { + impl.logger.Errorw("Failed to post to grafeas", "err", err) + return err + } + fmt.Print(resp) + fmt.Print(httpResponse) + */ + + url := fmt.Sprintf("%s/%s/occurrences", basePath, projectID) + b, err := json.Marshal(&occurrence) + if err != nil { + b = []byte("OK") + } + reqBody := []byte(b) + httpResponse, err := impl.httpPost(reqBody, url) + if err != nil { + impl.logger.Errorw("Failed to post to grafeas", "err", err) + return false, err + } + impl.logger.Error(httpResponse) + return true, nil +} + +func (impl *GrafeasServiceImpl) GetOccurrenceById(occurrenceID string) (*grafeas.V1beta1Occurrence, error) { + occurrenceID = "e187df4c-0f42-4951-a919-6ef5b7874176" + url := fmt.Sprintf("%s/%s/occurrences/%s", basePath, projectID, occurrenceID) + httpResponse, err := impl.httpGet(url) + if err != nil { + impl.logger.Errorw("Failed to get from grafeas", "err", err) + return nil, err + } + + defer httpResponse.Body.Close() + var occurrenceResponse grafeas.V1beta1Occurrence + resBody, err := ioutil.ReadAll(httpResponse.Body) + if err != nil { + impl.logger.Errorw("error in parsing response ", "err", err) + return nil, err + } + if httpResponse.StatusCode >= 200 && httpResponse.StatusCode <= 300 { + err = json.Unmarshal(resBody, &occurrenceResponse) + if err != nil { + impl.logger.Errorw("error in resp Unmarshal ", "err", err) + return nil, err + } + } + impl.logger.Errorw("api response", "status code", httpResponse.StatusCode) + impl.logger.Info(occurrenceResponse) + return &occurrenceResponse, nil +} + +func (impl *GrafeasServiceImpl) GetAllOccurrence() ([]*grafeas.V1beta1Occurrence, error) { + url := fmt.Sprintf("%s/%s/notes", basePath, projectID) + httpResponse, err := impl.httpGet(url) + if err != nil { + impl.logger.Errorw("Failed to get from grafeas", "err", err) + return nil, err + } + defer httpResponse.Body.Close() + var occurrenceResponse []*grafeas.V1beta1Occurrence + resBody, err := ioutil.ReadAll(httpResponse.Body) + if err != nil { + impl.logger.Errorw("error in parsing response ", "err", err) + return nil, err + } + if httpResponse.StatusCode >= 200 && httpResponse.StatusCode <= 300 { + err = json.Unmarshal(resBody, &occurrenceResponse) + if err != nil { + impl.logger.Errorw("error in resp Unmarshal ", "err", err) + return nil, err + } + } + impl.logger.Errorw("api response", "status code", httpResponse.StatusCode) + impl.logger.Info(occurrenceResponse) + return occurrenceResponse, nil +} + +func (impl *GrafeasServiceImpl) httpPost(reqBody []byte, url string) (*http.Response, error) { + impl.logger.Debugw("request", "body", string(reqBody)) + req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(reqBody)) + if err != nil { + impl.logger.Errorw("error while creating post request", "err", err) + return nil, err + } + req.Header.Set("Content-Type", "application/json") + resp, err := impl.httpClient.Do(req) + if err != nil { + impl.logger.Errorw("error while http Do request ", "err", err) + return nil, err + } + impl.logger.Infow("response from http Do request", "status code", resp.StatusCode) + return resp, err +} + +func (impl *GrafeasServiceImpl) httpGet(url string) (*http.Response, error) { + impl.logger.Debugw("request", "url", url) + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + impl.logger.Errorw("error while creating get request", "err", err) + return nil, err + } + req.Header.Set("Content-Type", "application/json") + resp, err := impl.httpClient.Do(req) + if err != nil { + impl.logger.Errorw("error while http Do request ", "err", err) + return nil, err + } + impl.logger.Infow("response from http Do request", "status code", resp.StatusCode) + return resp, err +} diff --git a/image-scanner/pkg/helper/helper.go b/image-scanner/pkg/helper/helper.go new file mode 100644 index 000000000..f21b35b12 --- /dev/null +++ b/image-scanner/pkg/helper/helper.go @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package helper + +import "os" + +func DoesFileExist(path string) (bool, error) { + _, err := os.Stat(path) + if err == nil { + return true, nil + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} diff --git a/image-scanner/pkg/klarService/KlarService.go b/image-scanner/pkg/klarService/KlarService.go new file mode 100644 index 000000000..f934198f6 --- /dev/null +++ b/image-scanner/pkg/klarService/KlarService.go @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package klarService + +import ( + "fmt" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ecr" + "github.com/devtron-labs/image-scanner/common" + "github.com/devtron-labs/image-scanner/pkg/security" + "github.com/devtron-labs/image-scanner/pkg/sql/bean" + "github.com/devtron-labs/image-scanner/pkg/sql/repository" + "github.com/go-pg/pg" + "strings" + + "errors" + "github.com/caarlos0/env/v6" + /*"github.com/devtron-labs/image-scanner/client"*/ + /*"github.com/devtron-labs/image-scanner/client"*/ + "github.com/devtron-labs/image-scanner/pkg/grafeasService" + "github.com/optiopay/klar/clair" + "github.com/optiopay/klar/docker" + "go.uber.org/zap" + "golang.org/x/oauth2/google" + "time" +) + +type KlarConfig struct { + //LayerCount int `env:"CLUSTER_ID" envDefault:"0"` + ClairAddr string `env:"CLAIR_ADDR" envDefault:"http://localhost:6060"` + //CLAIR_OUTPUT string `env:"CLAIR_OUTPUT" envDefault:"localhost:High"` + //CLAIR_THRESHOLD string `env:"CLAIR_THRESHOLD" envDefault:"localhost:10"` + ClairTimeout int `env:"CLAIR_TIMEOUT" envDefault:"30"` + //DOCKER_TIMEOUT string `env:"DOCKER_TIMEOUT" envDefault:"5"` + JSONOutput bool `env:"JSON_OUTPUT" envDefault:"true"` +} + +func GetKlarConfig() (*KlarConfig, error) { + cfg := &KlarConfig{} + err := env.Parse(cfg) + if err != nil { + return nil, errors.New("could not get event service url") + } + return cfg, err +} + +type KlarService interface { + Process(scanEvent *common.ImageScanEvent, executionHistory *repository.ImageScanExecutionHistory) (*common.ScanEventResponse, error) +} + +type KlarServiceImpl struct { + logger *zap.SugaredLogger + klarConfig *KlarConfig + grafeasService grafeasService.GrafeasService + userRepository repository.UserRepository + imageScanService security.ImageScanService + dockerArtifactStoreRepository repository.DockerArtifactStoreRepository + scanToolMetadataRepository repository.ScanToolMetadataRepository +} + +func NewKlarServiceImpl(logger *zap.SugaredLogger, klarConfig *KlarConfig, grafeasService grafeasService.GrafeasService, + userRepository repository.UserRepository, imageScanService security.ImageScanService, + dockerArtifactStoreRepository repository.DockerArtifactStoreRepository, + scanToolMetadataRepository repository.ScanToolMetadataRepository) *KlarServiceImpl { + return &KlarServiceImpl{ + logger: logger, + klarConfig: klarConfig, + grafeasService: grafeasService, + userRepository: userRepository, + imageScanService: imageScanService, + dockerArtifactStoreRepository: dockerArtifactStoreRepository, + scanToolMetadataRepository: scanToolMetadataRepository, + } +} + +func (impl *KlarServiceImpl) Process(scanEvent *common.ImageScanEvent, executionHistory *repository.ImageScanExecutionHistory) (*common.ScanEventResponse, error) { + scanEventResponse := &common.ScanEventResponse{ + RequestData: scanEvent, + } + dockerRegistry, err := impl.dockerArtifactStoreRepository.FindById(scanEvent.DockerRegistryId) + if err != nil { + impl.logger.Errorw("error in getting docker registry by id", "err", err, "id", scanEvent.DockerRegistryId) + return nil, err + } + _, scanned, err := impl.imageScanService.IsImageScanned(scanEvent.Image, false) + if err != nil && err != pg.ErrNoRows { + impl.logger.Errorw("error in fetching scan history ", "err", err) + return nil, err + } + if scanned { + impl.logger.Infow("image already scanned", "image", scanEvent.Image) + return scanEventResponse, nil + } + tokenGcr := "" + tokenData := "" + tokenAddr := &tokenData + if dockerRegistry.RegistryType == repository.REGISTRYTYPE_ECR { + accessKey, secretKey := dockerRegistry.AWSAccessKeyId, dockerRegistry.AWSSecretAccessKey + var creds *credentials.Credentials + if len(dockerRegistry.AWSAccessKeyId) == 0 || len(dockerRegistry.AWSSecretAccessKey) == 0 { + sess, err := session.NewSession(&aws.Config{ + Region: &dockerRegistry.AWSRegion, + }) + if err != nil { + impl.logger.Errorw("error in starting aws new session", "err", err) + return nil, err + } + creds = ec2rolecreds.NewCredentials(sess) + } else { + creds = credentials.NewStaticCredentials(accessKey, secretKey, "") + } + sess, err := session.NewSession(&aws.Config{ + Region: &dockerRegistry.AWSRegion, + Credentials: creds, + }) + if err != nil { + impl.logger.Errorw("error in starting aws new session", "err", err) + return nil, err + } + + // Create a ECR client with additional configuration + svc := ecr.New(sess, aws.NewConfig().WithRegion(dockerRegistry.AWSRegion)) + token, err := svc.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{}) + if err != nil { + impl.logger.Errorw("error in getting auth token from ecr", "err", err) + return nil, err + } + tokenAddr = token.AuthorizationData[0].AuthorizationToken + } else if dockerRegistry.Username == "_json_key" { + lenPassword := len(dockerRegistry.Password) + if lenPassword > 1 { + dockerRegistry.Password = strings.TrimPrefix(dockerRegistry.Password, "'") + dockerRegistry.Password = strings.TrimSuffix(dockerRegistry.Password, "'") + } + jwtToken, err := google.JWTAccessTokenSourceWithScope([]byte(dockerRegistry.Password), "") + if err != nil { + impl.logger.Errorw("error in getting token from json key file-gcr", "err", err) + return nil, err + } + token, err := jwtToken.Token() + if err != nil { + impl.logger.Errorw("error in getting token from jwt token", "err", err) + return nil, err + } + tokenGcr = fmt.Sprintf(token.TokenType + " " + token.AccessToken) + } + config := &docker.Config{ + ImageName: scanEvent.Image, + User: dockerRegistry.Username, + Password: dockerRegistry.Password, + Token: *tokenAddr, + //InsecureRegistry: true, + //InsecureTLS: true, + Timeout: 4 * time.Minute, + } + impl.logger.Debugw("config", "config", config) + image, err := docker.NewImage(config) + if err != nil { + impl.logger.Errorw("Can't parse name", "err", err) + return scanEventResponse, err + } + if tokenGcr != "" { + //setting token here because docker.NewImage sets the token as basic and in gcp it's bearer in most of the cases + image.Token = tokenGcr + } + err = image.Pull() + if err != nil { + impl.logger.Errorw("Can't pull image ", "err", err) + return scanEventResponse, err + } + impl.logger.Debugw("image pull", "layers count", len(image.FsLayers)) + output := jsonOutput{ + Vulnerabilities: make(map[string][]*clair.Vulnerability), + } + + if len(image.FsLayers) == 0 { + impl.logger.Error("Can't pull fsLayers") + return scanEventResponse, errors.New("can't pull fsLayers") + } else { + if impl.klarConfig.JSONOutput { + output.LayerCount = len(image.FsLayers) + } else { + impl.logger.Debugw("Analysing layers ", "layers", len(image.FsLayers)) + } + } + + var vs []*clair.Vulnerability + for _, ver := range []int{1, 3} { + c := clair.NewClair(impl.klarConfig.ClairAddr, ver, time.Duration(5*time.Minute)) + vs, err = c.Analyse(image) + if err != nil { + impl.logger.Errorw("Failed to analyze using API", "ver", ver, "err", err) + } else { + if !impl.klarConfig.JSONOutput { + impl.logger.Debugw("Got results from Clair API ", "ver", ver) + } + break + } + } + if err != nil { + impl.logger.Errorw("Failed to analyze, exiting", "err", err) + return scanEventResponse, err + } + tool, err := impl.scanToolMetadataRepository.FindByNameAndVersion(bean.ScanToolClair, bean.ScanToolVersion2) + if err != nil { + impl.logger.Errorw("error in getting tool by name and version", "err", err) + return scanEventResponse, err + } + vulnerabilities, err := impl.imageScanService.CreateScanExecutionRegistryForClairV2(vs, scanEvent, tool.Id, executionHistory) + if err != nil { + impl.logger.Errorw("Failed dump scanned data", "err", err) + return scanEventResponse, err + } + scanEventResponse.ResponseDataClairV2 = vulnerabilities + + return scanEventResponse, nil +} + +type jsonOutput struct { + LayerCount int + Vulnerabilities map[string][]*clair.Vulnerability +} diff --git a/image-scanner/pkg/logger/logger.go b/image-scanner/pkg/logger/logger.go new file mode 100644 index 000000000..ab81aa55d --- /dev/null +++ b/image-scanner/pkg/logger/logger.go @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package logger + +import ( + "fmt" + "github.com/caarlos0/env/v6" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "net/http" +) + +func NewSugardLogger() *zap.SugaredLogger { + l, err := InitLogger() + if err != nil { + panic("failed to create the default logger: " + err.Error()) + } + return l +} + +func NewHttpClient() *http.Client { + return http.DefaultClient +} + +type LogConfig struct { + Level int `env:"LOG_LEVEL" envDefault:"0"` // default info +} + +func InitLogger() (*zap.SugaredLogger, error) { + cfg := &LogConfig{} + err := env.Parse(cfg) + if err != nil { + fmt.Println("failed to parse logger env config: " + err.Error()) + return nil, err + } + + config := zap.NewProductionConfig() + config.Level = zap.NewAtomicLevelAt(zapcore.Level(cfg.Level)) + l, err := config.Build() + if err != nil { + fmt.Println("failed to create the default logger: " + err.Error()) + return nil, err + } + logger := l.Sugar() + return logger, nil +} diff --git a/image-scanner/pkg/middleware/delegator.go b/image-scanner/pkg/middleware/delegator.go new file mode 100644 index 000000000..e04c3bae9 --- /dev/null +++ b/image-scanner/pkg/middleware/delegator.go @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2020-2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package middleware + +import ( + "bufio" + "io" + "net" + "net/http" +) + +const ( + closeNotifier = 1 << iota + flusher + hijacker + readerFrom + pusher +) + +type delegator interface { + http.ResponseWriter + + Status() int + Written() int64 +} + +type responseWriterDelegator struct { + http.ResponseWriter + + status int + written int64 + wroteHeader bool + observeWriteHeader func(int) +} + +func (r *responseWriterDelegator) Status() int { + return r.status +} + +func (r *responseWriterDelegator) Written() int64 { + return r.written +} + +func (r *responseWriterDelegator) WriteHeader(code int) { + if r.observeWriteHeader != nil && !r.wroteHeader { + // Only call observeWriteHeader for the 1st time. It's a bug if + // WriteHeader is called more than once, but we want to protect + // against it here. Note that we still delegate the WriteHeader + // to the original ResponseWriter to not mask the bug from it. + r.observeWriteHeader(code) + } + r.status = code + r.wroteHeader = true + r.ResponseWriter.WriteHeader(code) +} + +func (r *responseWriterDelegator) Write(b []byte) (int, error) { + // If applicable, call WriteHeader here so that observeWriteHeader is + // handled appropriately. + if !r.wroteHeader { + r.WriteHeader(http.StatusOK) + } + n, err := r.ResponseWriter.Write(b) + r.written += int64(n) + return n, err +} + +type closeNotifierDelegator struct{ *responseWriterDelegator } +type flusherDelegator struct{ *responseWriterDelegator } +type hijackerDelegator struct{ *responseWriterDelegator } +type readerFromDelegator struct{ *responseWriterDelegator } +type pusherDelegator struct{ *responseWriterDelegator } + +func (d closeNotifierDelegator) CloseNotify() <-chan bool { + //lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to + //remove support from client_golang yet. + return d.ResponseWriter.(http.CloseNotifier).CloseNotify() +} +func (d flusherDelegator) Flush() { + // If applicable, call WriteHeader here so that observeWriteHeader is + // handled appropriately. + if !d.wroteHeader { + d.WriteHeader(http.StatusOK) + } + d.ResponseWriter.(http.Flusher).Flush() +} +func (d hijackerDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) { + return d.ResponseWriter.(http.Hijacker).Hijack() +} +func (d readerFromDelegator) ReadFrom(re io.Reader) (int64, error) { + // If applicable, call WriteHeader here so that observeWriteHeader is + // handled appropriately. + if !d.wroteHeader { + d.WriteHeader(http.StatusOK) + } + n, err := d.ResponseWriter.(io.ReaderFrom).ReadFrom(re) + d.written += n + return n, err +} +func (d pusherDelegator) Push(target string, opts *http.PushOptions) error { + return d.ResponseWriter.(http.Pusher).Push(target, opts) +} + +var pickDelegator = make([]func(*responseWriterDelegator) delegator, 32) + +func init() { + // TODO(beorn7): Code generation would help here. + pickDelegator[0] = func(d *responseWriterDelegator) delegator { // 0 + return d + } + pickDelegator[closeNotifier] = func(d *responseWriterDelegator) delegator { // 1 + return closeNotifierDelegator{d} + } + pickDelegator[flusher] = func(d *responseWriterDelegator) delegator { // 2 + return flusherDelegator{d} + } + pickDelegator[flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 3 + return struct { + *responseWriterDelegator + http.Flusher + http.CloseNotifier + }{d, flusherDelegator{d}, closeNotifierDelegator{d}} + } + pickDelegator[hijacker] = func(d *responseWriterDelegator) delegator { // 4 + return hijackerDelegator{d} + } + pickDelegator[hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 5 + return struct { + *responseWriterDelegator + http.Hijacker + http.CloseNotifier + }{d, hijackerDelegator{d}, closeNotifierDelegator{d}} + } + pickDelegator[hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 6 + return struct { + *responseWriterDelegator + http.Hijacker + http.Flusher + }{d, hijackerDelegator{d}, flusherDelegator{d}} + } + pickDelegator[hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 7 + return struct { + *responseWriterDelegator + http.Hijacker + http.Flusher + http.CloseNotifier + }{d, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} + } + pickDelegator[readerFrom] = func(d *responseWriterDelegator) delegator { // 8 + return readerFromDelegator{d} + } + pickDelegator[readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 9 + return struct { + *responseWriterDelegator + io.ReaderFrom + http.CloseNotifier + }{d, readerFromDelegator{d}, closeNotifierDelegator{d}} + } + pickDelegator[readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 10 + return struct { + *responseWriterDelegator + io.ReaderFrom + http.Flusher + }{d, readerFromDelegator{d}, flusherDelegator{d}} + } + pickDelegator[readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 11 + return struct { + *responseWriterDelegator + io.ReaderFrom + http.Flusher + http.CloseNotifier + }{d, readerFromDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} + } + pickDelegator[readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 12 + return struct { + *responseWriterDelegator + io.ReaderFrom + http.Hijacker + }{d, readerFromDelegator{d}, hijackerDelegator{d}} + } + pickDelegator[readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 13 + return struct { + *responseWriterDelegator + io.ReaderFrom + http.Hijacker + http.CloseNotifier + }{d, readerFromDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}} + } + pickDelegator[readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 14 + return struct { + *responseWriterDelegator + io.ReaderFrom + http.Hijacker + http.Flusher + }{d, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}} + } + pickDelegator[readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 15 + return struct { + *responseWriterDelegator + io.ReaderFrom + http.Hijacker + http.Flusher + http.CloseNotifier + }{d, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} + } + pickDelegator[pusher] = func(d *responseWriterDelegator) delegator { // 16 + return pusherDelegator{d} + } + pickDelegator[pusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 17 + return struct { + *responseWriterDelegator + http.Pusher + http.CloseNotifier + }{d, pusherDelegator{d}, closeNotifierDelegator{d}} + } + pickDelegator[pusher+flusher] = func(d *responseWriterDelegator) delegator { // 18 + return struct { + *responseWriterDelegator + http.Pusher + http.Flusher + }{d, pusherDelegator{d}, flusherDelegator{d}} + } + pickDelegator[pusher+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 19 + return struct { + *responseWriterDelegator + http.Pusher + http.Flusher + http.CloseNotifier + }{d, pusherDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} + } + pickDelegator[pusher+hijacker] = func(d *responseWriterDelegator) delegator { // 20 + return struct { + *responseWriterDelegator + http.Pusher + http.Hijacker + }{d, pusherDelegator{d}, hijackerDelegator{d}} + } + pickDelegator[pusher+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 21 + return struct { + *responseWriterDelegator + http.Pusher + http.Hijacker + http.CloseNotifier + }{d, pusherDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}} + } + pickDelegator[pusher+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 22 + return struct { + *responseWriterDelegator + http.Pusher + http.Hijacker + http.Flusher + }{d, pusherDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}} + } + pickDelegator[pusher+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { //23 + return struct { + *responseWriterDelegator + http.Pusher + http.Hijacker + http.Flusher + http.CloseNotifier + }{d, pusherDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} + } + pickDelegator[pusher+readerFrom] = func(d *responseWriterDelegator) delegator { // 24 + return struct { + *responseWriterDelegator + http.Pusher + io.ReaderFrom + }{d, pusherDelegator{d}, readerFromDelegator{d}} + } + pickDelegator[pusher+readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 25 + return struct { + *responseWriterDelegator + http.Pusher + io.ReaderFrom + http.CloseNotifier + }{d, pusherDelegator{d}, readerFromDelegator{d}, closeNotifierDelegator{d}} + } + pickDelegator[pusher+readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 26 + return struct { + *responseWriterDelegator + http.Pusher + io.ReaderFrom + http.Flusher + }{d, pusherDelegator{d}, readerFromDelegator{d}, flusherDelegator{d}} + } + pickDelegator[pusher+readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 27 + return struct { + *responseWriterDelegator + http.Pusher + io.ReaderFrom + http.Flusher + http.CloseNotifier + }{d, pusherDelegator{d}, readerFromDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} + } + pickDelegator[pusher+readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 28 + return struct { + *responseWriterDelegator + http.Pusher + io.ReaderFrom + http.Hijacker + }{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}} + } + pickDelegator[pusher+readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 29 + return struct { + *responseWriterDelegator + http.Pusher + io.ReaderFrom + http.Hijacker + http.CloseNotifier + }{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}} + } + pickDelegator[pusher+readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 30 + return struct { + *responseWriterDelegator + http.Pusher + io.ReaderFrom + http.Hijacker + http.Flusher + }{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}} + } + pickDelegator[pusher+readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 31 + return struct { + *responseWriterDelegator + http.Pusher + io.ReaderFrom + http.Hijacker + http.Flusher + http.CloseNotifier + }{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}} + } +} + +func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) delegator { + d := &responseWriterDelegator{ + ResponseWriter: w, + observeWriteHeader: observeWriteHeaderFunc, + } + + id := 0 + //lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to + //remove support from client_golang yet. + if _, ok := w.(http.CloseNotifier); ok { + id += closeNotifier + } + if _, ok := w.(http.Flusher); ok { + id += flusher + } + if _, ok := w.(http.Hijacker); ok { + id += hijacker + } + if _, ok := w.(io.ReaderFrom); ok { + id += readerFrom + } + if _, ok := w.(http.Pusher); ok { + id += pusher + } + + return pickDelegator[id](d) +} diff --git a/image-scanner/pkg/middleware/instrument.go b/image-scanner/pkg/middleware/instrument.go new file mode 100644 index 000000000..7c11c9d5c --- /dev/null +++ b/image-scanner/pkg/middleware/instrument.go @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020-2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package middleware + +import ( + "github.com/gorilla/mux" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "net/http" + "strconv" + "time" +) + +var ( + httpDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "image_scanner_http_duration_seconds", + Help: "Duration of HTTP requests.", + }, []string{"path", "method", "status"}) +) + +var requestCounter = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "image_scanner_http_requests_total", + Help: "How many HTTP requests processed, partitioned by status code, method and HTTP path.", + }, + []string{"path", "method", "status"}) + +var PgQueryDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "pg_query_duration_seconds", + Help: "Duration of PG queries", +}, []string{"label"}) + +var currentRequestGauge = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Name: "image_scanner_http_requests_current", + Help: "no of request being served currently", +}, []string{"path", "method"}) + +var imageScannerHTTPStatusCode = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "image_scanner_http_status_code", + Help: "http status code", +}, []string{"path", "method", "status"}) + +// prometheusMiddleware implements mux.MiddlewareFunc. +func PrometheusMiddleware(next http.Handler) http.Handler { + // prometheus.MustRegister(requestCounter) + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + route := mux.CurrentRoute(r) + path, _ := route.GetPathTemplate() + method := r.Method + g := currentRequestGauge.WithLabelValues(path, method) + g.Inc() + defer g.Dec() + d := newDelegator(w, nil) + next.ServeHTTP(d, r) + httpDuration.WithLabelValues(path, method, strconv.Itoa(d.Status())).Observe(time.Since(start).Seconds()) + requestCounter.WithLabelValues(path, method, strconv.Itoa(d.Status())).Inc() + imageScannerHTTPStatusCode.WithLabelValues(path, method, strconv.Itoa(d.Status())) + }) +} diff --git a/image-scanner/pkg/roundTripper/RoundTripperService.go b/image-scanner/pkg/roundTripper/RoundTripperService.go new file mode 100644 index 000000000..3a1f3d825 --- /dev/null +++ b/image-scanner/pkg/roundTripper/RoundTripperService.go @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package roundTripper + +import ( + "context" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ecr" + "github.com/devtron-labs/image-scanner/common" + "github.com/devtron-labs/image-scanner/pkg/security" + "github.com/devtron-labs/image-scanner/pkg/sql/repository" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "go.uber.org/zap" + "net/http" + "strings" + "sync" +) + +type RoundTripperService interface { + GetRoundTripper(scanEvent *common.ImageScanEvent) (http.RoundTripper, error) +} +type RoundTripperServiceImpl struct { + Logger *zap.SugaredLogger + DockerArtifactStoreRepository repository.DockerArtifactStoreRepository + imageScanService security.ImageScanService +} + +func NewRoundTripperServiceImpl(logger *zap.SugaredLogger, + DockerArtifactStoreRepository repository.DockerArtifactStoreRepository) *RoundTripperServiceImpl { + return &RoundTripperServiceImpl{ + Logger: logger, + DockerArtifactStoreRepository: DockerArtifactStoreRepository, + } +} + +type RoundTripperConfig struct { + Username string + Password string + ProxyUrl string +} + +var ( + rtMu sync.Mutex +) + +const ( + userAgent = `clairctl/1` +) + +func (impl *RoundTripperServiceImpl) GetRoundTripper(scanEvent *common.ImageScanEvent) (http.RoundTripper, error) { + authenticator, dockerRegistry, err := impl.GetAuthenticatorByDockerRegistryId(scanEvent.DockerRegistryId) + if err != nil { + impl.Logger.Errorw("error, GetAuthenticatorByDockerRegistryId", "err", err, "dockerRegistryId", scanEvent.DockerRegistryId) + return nil, err + } + proxyUrl, referenceOptions, err := impl.imageScanService.FetchProxyUrl(scanEvent) + if err != nil { + impl.Logger.Errorw("error, FetchProxyUrl", "err", err, "dockerRegistryId", scanEvent.DockerRegistryId) + return nil, err + } + rtConfig := &RoundTripperConfig{ + Username: dockerRegistry.Username, + Password: dockerRegistry.Password, + ProxyUrl: proxyUrl, + } + rt, err := impl.GetRoundTripperTransport(rtConfig) + if err != nil { + impl.Logger.Errorw("error in getting roundTripper", "err", err) + return nil, err + } + return impl.UpdateTransportWithReference(rt, scanEvent.Image, authenticator, referenceOptions) +} + +func (impl *RoundTripperServiceImpl) GetRoundTripperTransport(config *RoundTripperConfig) (http.RoundTripper, error) { + return http.DefaultTransport, nil +} + +func (impl *RoundTripperServiceImpl) GetAuthenticatorByDockerRegistryId(dockerRegistryId string) (authn.Authenticator, *repository.DockerArtifactStore, error) { + dockerRegistry, err := impl.DockerArtifactStoreRepository.FindById(dockerRegistryId) + if err != nil { + impl.Logger.Errorw("error in getting docker registry by id", "err", err, "id", dockerRegistryId) + return nil, nil, err + } + //case for gcr and artifact registry + if dockerRegistry.Username == "_json_key" { + lenPassword := len(dockerRegistry.Password) + if lenPassword > 1 { + dockerRegistry.Password = strings.TrimPrefix(dockerRegistry.Password, "'") + dockerRegistry.Password = strings.TrimSuffix(dockerRegistry.Password, "'") + } + } + authConfig := authn.AuthConfig{ + Username: dockerRegistry.Username, + Password: dockerRegistry.Password, + } + if dockerRegistry.RegistryType == repository.REGISTRYTYPE_ECR { + accessKey, secretKey := dockerRegistry.AWSAccessKeyId, dockerRegistry.AWSSecretAccessKey + var creds *credentials.Credentials + if len(dockerRegistry.AWSAccessKeyId) == 0 || len(dockerRegistry.AWSSecretAccessKey) == 0 { + sess, err := session.NewSession(&aws.Config{ + Region: &dockerRegistry.AWSRegion, + }) + if err != nil { + impl.Logger.Errorw("error in starting aws new session", "err", err) + return nil, nil, err + } + creds = ec2rolecreds.NewCredentials(sess) + } else { + creds = credentials.NewStaticCredentials(accessKey, secretKey, "") + } + sess, err := session.NewSession(&aws.Config{ + Region: &dockerRegistry.AWSRegion, + Credentials: creds, + }) + if err != nil { + impl.Logger.Errorw("error in starting aws new session", "err", err) + return nil, nil, err + } + + // Create a ECR client with additional configuration + svc := ecr.New(sess, aws.NewConfig().WithRegion(dockerRegistry.AWSRegion)) + token, err := svc.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{}) + if err != nil { + impl.Logger.Errorw("error in getting auth token from ecr", "err", err) + return nil, nil, err + } + authConfig.Auth = *token.AuthorizationData[0].AuthorizationToken + } + authenticatorFromConfig := authn.FromConfig(authConfig) + return authenticatorFromConfig, dockerRegistry, nil +} +func (impl *RoundTripperServiceImpl) UpdateTransportWithReference(rt http.RoundTripper, ref string, authenticator authn.Authenticator, referenceOptions []name.Option) (http.RoundTripper, error) { + r, err := name.ParseReference(ref, referenceOptions...) + if err != nil { + impl.Logger.Errorw("error in parsing reference", "err", err, "ref", ref) + return nil, err + } + repo := r.Context() + rtMu.Lock() + defer rtMu.Unlock() + rt = transport.NewUserAgent(rt, userAgent) + rt = transport.NewRetry(rt) + rt, err = transport.NewWithContext(context.Background(), repo.Registry, authenticator, rt, []string{repo.Scope(transport.PullScope)}) + if err != nil { + impl.Logger.Errorw("error in getting roundTripper", "err", err) + return nil, err + } + return rt, nil +} diff --git a/image-scanner/pkg/security/ImageScanService.go b/image-scanner/pkg/security/ImageScanService.go new file mode 100644 index 000000000..413ee9da0 --- /dev/null +++ b/image-scanner/pkg/security/ImageScanService.go @@ -0,0 +1,1015 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package security + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "github.com/Knetic/govaluate" + "github.com/caarlos0/env" + "github.com/devtron-labs/image-scanner/common" + cliUtil "github.com/devtron-labs/image-scanner/internals/step-lib/util/cli-util" + commonUtil "github.com/devtron-labs/image-scanner/internals/step-lib/util/common-util" + httpUtil "github.com/devtron-labs/image-scanner/internals/step-lib/util/http-util" + "github.com/devtron-labs/image-scanner/internals/util" + "github.com/devtron-labs/image-scanner/pkg/helper" + "github.com/devtron-labs/image-scanner/pkg/sql/bean" + "github.com/devtron-labs/image-scanner/pkg/sql/repository" + "github.com/go-pg/pg" + "github.com/google/go-containerregistry/pkg/name" + "github.com/optiopay/klar/clair" + "github.com/quay/claircore" + "github.com/tidwall/gjson" + "go.uber.org/zap" + "net/url" + "os" + "path" + "path/filepath" + "strconv" + "sync" + "text/template" + "time" +) + +type ImageScanService interface { + ScanImage(scanEvent *common.ImageScanEvent, tool *repository.ScanToolMetadata, executionHistory *repository.ImageScanExecutionHistory, executionHistoryDirPath string) error + CreateScanExecutionRegistryForClairV4(vs []*claircore.Vulnerability, event *common.ImageScanEvent, toolId int, executionHistory *repository.ImageScanExecutionHistory) ([]*claircore.Vulnerability, error) + CreateScanExecutionRegistryForClairV2(vs []*clair.Vulnerability, event *common.ImageScanEvent, toolId int, executionHistory *repository.ImageScanExecutionHistory) ([]*clair.Vulnerability, error) + IsImageScanned(image string, hasSource bool) (int, bool, error) + ScanImageForTool(tool *repository.ScanToolMetadata, executionHistoryId int, executionHistoryDirPathCopy string, wg *sync.WaitGroup, userId int32, ctx context.Context, imageScanRenderDto *common.ImageScanRenderDto, isV2 bool) (string, string, error) + CreateFolderForOutputData(executionHistoryModelId int) string + HandleProgressingScans() + GetActiveTool() (*repository.ScanToolMetadata, error) + RegisterScanExecutionHistoryAndState(executionHistoryModel *repository.ImageScanExecutionHistory, tool *repository.ScanToolMetadata) (*repository.ImageScanExecutionHistory, string, error) + GetImageScanRenderDto(registryId string, scanEvent *common.ImageScanEvent) (*common.ImageScanRenderDto, error) + GetImageToBeScannedAndFetchCliEnv(scanEvent *common.ImageScanEvent) (string, error) + FetchProxyUrl(scanEvent *common.ImageScanEvent) (string, []name.Option, error) +} + +type ImageScanServiceImpl struct { + Logger *zap.SugaredLogger + ScanHistoryRepository repository.ImageScanHistoryRepository + ScanResultRepository repository.ImageScanResultRepository + ScanObjectMetaRepository repository.ImageScanObjectMetaRepository + CveStoreRepository repository.CveStoreRepository + ImageScanDeployInfoRepository repository.ImageScanDeployInfoRepository + CiArtifactRepository repository.CiArtifactRepository + ScanToolExecutionHistoryMappingRepository repository.ScanToolExecutionHistoryMappingRepository + ScanToolMetadataRepository repository.ScanToolMetadataRepository + ScanStepConditionRepository repository.ScanStepConditionRepository + ScanToolStepRepository repository.ScanToolStepRepository + ScanStepConditionMappingRepository repository.ScanStepConditionMappingRepository + ImageScanConfig *ImageScanConfig + DockerArtifactStoreRepository repository.DockerArtifactStoreRepository + RegistryIndexMappingRepository repository.RegistryIndexMappingRepository + CliCommandEnv []string +} + +func NewImageScanServiceImpl(logger *zap.SugaredLogger, scanHistoryRepository repository.ImageScanHistoryRepository, + scanResultRepository repository.ImageScanResultRepository, scanObjectMetaRepository repository.ImageScanObjectMetaRepository, + cveStoreRepository repository.CveStoreRepository, imageScanDeployInfoRepository repository.ImageScanDeployInfoRepository, + ciArtifactRepository repository.CiArtifactRepository, + scanToolExecutionHistoryMappingRepository repository.ScanToolExecutionHistoryMappingRepository, + scanToolMetadataRepository repository.ScanToolMetadataRepository, + scanStepConditionRepository repository.ScanStepConditionRepository, + scanToolStepRepository repository.ScanToolStepRepository, + scanStepConditionMappingRepository repository.ScanStepConditionMappingRepository, + imageScanConfig *ImageScanConfig, + dockerArtifactStoreRepository repository.DockerArtifactStoreRepository, registryIndexMappingRepository repository.RegistryIndexMappingRepository) *ImageScanServiceImpl { + imageScanService := &ImageScanServiceImpl{Logger: logger, ScanHistoryRepository: scanHistoryRepository, ScanResultRepository: scanResultRepository, + ScanObjectMetaRepository: scanObjectMetaRepository, CveStoreRepository: cveStoreRepository, + ImageScanDeployInfoRepository: imageScanDeployInfoRepository, + CiArtifactRepository: ciArtifactRepository, + ScanToolExecutionHistoryMappingRepository: scanToolExecutionHistoryMappingRepository, + ScanToolMetadataRepository: scanToolMetadataRepository, + ScanStepConditionRepository: scanStepConditionRepository, + ScanToolStepRepository: scanToolStepRepository, + ScanStepConditionMappingRepository: scanStepConditionMappingRepository, + ImageScanConfig: imageScanConfig, + DockerArtifactStoreRepository: dockerArtifactStoreRepository, + RegistryIndexMappingRepository: registryIndexMappingRepository, + CliCommandEnv: os.Environ(), + } + imageScanService.HandleProgressingScans() + return imageScanService +} + +func (impl *ImageScanServiceImpl) GetImageToBeScannedAndFetchCliEnv(scanEvent *common.ImageScanEvent) (string, error) { + impl.CliCommandEnv = append(os.Environ(), impl.CliCommandEnv...) + return scanEvent.Image, nil +} + +func (impl *ImageScanServiceImpl) GetActiveTool() (*repository.ScanToolMetadata, error) { + //get active tool + tool, err := impl.ScanToolMetadataRepository.FindActiveToolByScanTarget(repository.ImageScanTargetType) + if err != nil { + impl.Logger.Errorw("error in getting active tool by scan target", "scanTarget", repository.ImageScanTargetType, "err", err) + return nil, err + } + return tool, nil +} + +func (impl *ImageScanServiceImpl) CreateCaCertFile(cert string) (string, error) { + // creating directory for temporarily storing CA certs, if not exist + isExist, err := helper.DoesFileExist(common.CaCertDirectory) + if err != nil { + impl.Logger.Errorw("error in checking if certs directory exist ", "err", err) + return "", err + } + if !isExist { + err = os.Mkdir(common.CaCertDirectory, commonUtil.DefaultFileCreatePermission) + if err != nil && os.IsNotExist(err) { + impl.Logger.Errorw("error in creating certs directory", "err", err) + return "", err + } + } + + caCertFilename := fmt.Sprintf("%s%v.pem", common.RegistryCaCertFilePrefix, util.Generate(6)) + caCertFilePath := filepath.Join(common.CaCertDirectory, caCertFilename) + // creating ca cert file + caCertFile, err := os.Create(caCertFilePath) + if err != nil { + impl.Logger.Errorw("error in creating cert file", "err", err) + return "", err + } + + // writing file with given cert + _, err = caCertFile.WriteString(cert) + if err != nil { + impl.Logger.Errorw("error in writing cert file", "err", err) + err := os.Remove(caCertFilePath) + if err != nil { + impl.Logger.Errorw("error in removing cert file", "err", err) + return "", err + } + return "", err + } + return caCertFilePath, nil +} + +func (impl *ImageScanServiceImpl) ScanImage(scanEvent *common.ImageScanEvent, tool *repository.ScanToolMetadata, executionHistory *repository.ImageScanExecutionHistory, executionHistoryDirPath string) error { + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(impl.ImageScanConfig.ScanImageTimeout)*time.Minute) + defer cancel() + //checking if image is already scanned or not + _, isImageScanned, err := impl.IsImageScanned(scanEvent.Image, false) + if err != nil && err != pg.ErrNoRows { + impl.Logger.Errorw("error in fetching scan history ", "image", scanEvent.Image, "err", err) + return err + } + if isImageScanned { + impl.Logger.Infow("image already scanned, skipping further process", "image", scanEvent.Image) + return nil + } + var caCertFilePath string + if scanEvent.DockerConnection == common.SECUREWITHCERT { + caCertFilePath, err = impl.CreateCaCertFile(scanEvent.DockerCert) + if err != nil { + impl.Logger.Errorw("error in creating cert file", "image", scanEvent.Image, "err", err) + return err + } + defer os.Remove(caCertFilePath) + } + imageScanRenderDto, err := impl.GetImageScanRenderDto(scanEvent.DockerRegistryId, scanEvent) + if err != nil { + impl.Logger.Errorw("service error, GetImageScanRenderDto", "dockerRegistryId", scanEvent.DockerRegistryId, "err", err) + return err + } + imageScanRenderDto.CaCertFilePath = caCertFilePath + wg := &sync.WaitGroup{} + wg.Add(1) + // TODO: if multiple processes are to be done in parallel, then error propagation should have to be done via channels + _, _, err = impl.ScanImageForTool(tool, executionHistory.Id, executionHistoryDirPath, wg, int32(scanEvent.UserId), ctx, imageScanRenderDto, false) + if err != nil { + impl.Logger.Errorw("err in scanning image", "tool", tool, "executionHistory.Id", executionHistory.Id, "executionHistoryDirPath", executionHistoryDirPath, "scanEvent.UserId", scanEvent.UserId, "err", err) + return err + } + wg.Wait() + return err +} + +func (impl *ImageScanServiceImpl) GetImageScanRenderDto(registryId string, scanEvent *common.ImageScanEvent) (*common.ImageScanRenderDto, error) { + dockerRegistry, err := impl.DockerArtifactStoreRepository.FindById(registryId) + if err == pg.ErrNoRows { + dockerRegistry = &repository.DockerArtifactStore{} + } else if err != nil { + impl.Logger.Errorw("error in getting docker registry by id", "id", registryId, "err", err) + return nil, err + } + imageScanRenderDto := &common.ImageScanRenderDto{ + RegistryType: dockerRegistry.RegistryType, + Username: dockerRegistry.Username, + Password: dockerRegistry.Password, + AWSAccessKeyId: dockerRegistry.AWSAccessKeyId, + AWSSecretAccessKey: dockerRegistry.AWSSecretAccessKey, + AWSRegion: dockerRegistry.AWSRegion, + Image: scanEvent.Image, + DockerConnection: scanEvent.DockerConnection, + } + return imageScanRenderDto, nil +} +func (impl *ImageScanServiceImpl) ScanImageForTool(tool *repository.ScanToolMetadata, executionHistoryId int, executionHistoryDirPathCopy string, wg *sync.WaitGroup, userId int32, ctx context.Context, imageScanRenderDto *common.ImageScanRenderDto, isV2 bool) (string, string, error) { + toolCopy := *tool + var processedState bean.ScanExecutionProcessState + err := impl.ProcessScanForTool(toolCopy, executionHistoryDirPathCopy, executionHistoryId, userId, ctx, imageScanRenderDto) + var errorMessage string + if err != nil { + impl.Logger.Errorw("error in processing scan for tool:", "toolCopy Name", toolCopy.Name, "err", err) + processedState = bean.ScanExecutionProcessStateFailed + errorMessage = err.Error() + } else { + processedState = bean.ScanExecutionProcessStateCompleted + } + updateErr := impl.ScanToolExecutionHistoryMappingRepository.UpdateStateByToolAndExecutionHistoryId(executionHistoryId, toolCopy.Id, processedState, time.Now(), errorMessage) + if updateErr != nil { + impl.Logger.Errorw("error in UpdateStateByToolAndExecutionHistoryId", "err", err) + err = updateErr + } + wg.Done() + return "", "", err +} +func (impl *ImageScanServiceImpl) CreateFolderForOutputData(executionHistoryModelId int) string { + executionHistoryModelIdStr := strconv.Itoa(executionHistoryModelId) + executionHistoryDirPath := path.Join(bean.ScanOutputDirectory, executionHistoryModelIdStr) + return executionHistoryDirPath +} + +func (impl *ImageScanServiceImpl) RegisterScanExecutionHistoryAndState(executionHistoryModel *repository.ImageScanExecutionHistory, + tool *repository.ScanToolMetadata) (*repository.ImageScanExecutionHistory, string, error) { + executionHistoryDirPath := "" + //creating execution history + tx, err := impl.ScanHistoryRepository.GetConnection().Begin() + if err != nil { + impl.Logger.Errorw("error in initiating db transaction", "err", err) + return nil, executionHistoryDirPath, err + } + // Rollback tx on error. + defer tx.Rollback() + err = impl.ScanHistoryRepository.Save(tx, executionHistoryModel) + if err != nil { + impl.Logger.Errorw("Failed to save executionHistory", "model", executionHistoryModel, "err", err) + return nil, executionHistoryDirPath, err + } + + // creating folder for storing all details if not exist + isExist, err := helper.DoesFileExist(bean.ScanOutputDirectory) + if err != nil { + impl.Logger.Errorw("error in checking if scan output directory exist ", "err", err) + return nil, executionHistoryDirPath, err + } + if !isExist { + err = os.Mkdir(bean.ScanOutputDirectory, commonUtil.DefaultFileCreatePermission) + if err != nil && !os.IsExist(err) { + impl.Logger.Errorw("error in creating Output directory", "toolId", tool.Id, "executionHistoryDir", executionHistoryDirPath, "err", err) + return nil, executionHistoryDirPath, err + } + } + // creating folder for storing output data for this execution history data + executionHistoryDirPath = impl.CreateFolderForOutputData(executionHistoryModel.Id) + err = os.Mkdir(executionHistoryDirPath, commonUtil.DefaultFileCreatePermission) + if err != nil && !os.IsExist(err) { + impl.Logger.Errorw("error in creating executionHistory directory", "executionHistoryId", executionHistoryModel.Id, "err", err) + return nil, executionHistoryDirPath, err + } + executionHistoryMappingModel := &repository.ScanToolExecutionHistoryMapping{ + ImageScanExecutionHistoryId: executionHistoryModel.Id, + ScanToolId: tool.Id, + ExecutionStartTime: executionHistoryModel.ExecutionTime, + State: bean.ScanExecutionProcessStateRunning, + AuditLog: repository.AuditLog{ + CreatedOn: executionHistoryModel.ExecutionTime, + CreatedBy: int32(executionHistoryModel.ExecutedBy), + UpdatedOn: executionHistoryModel.ExecutionTime, + UpdatedBy: int32(executionHistoryModel.ExecutedBy), + }, + } + err = impl.ScanToolExecutionHistoryMappingRepository.Save(tx, executionHistoryMappingModel) + if err != nil { + impl.Logger.Errorw("Failed to save executionHistoryMappingModel", "err", err) + return nil, executionHistoryDirPath, err + } + err = tx.Commit() + if err != nil { + impl.Logger.Errorw("error in committing transaction", "err", err) + return nil, executionHistoryDirPath, err + } + return executionHistoryModel, executionHistoryDirPath, nil +} + +func (impl *ImageScanServiceImpl) ProcessScanForTool(tool repository.ScanToolMetadata, executionHistoryDirPath string, executionHistoryId int, userId int32, ctx context.Context, imageScanRenderDto *common.ImageScanRenderDto) error { + imageScanConfig := &ImageScanConfig{} + err := env.Parse(imageScanConfig) + if err != nil { + impl.Logger.Errorw("error in parsing env ", "err", err) + return err + } + + // creating folder for storing this tool output data + toolIdStr := strconv.Itoa(tool.Id) + toolOutputDirPath := path.Join(executionHistoryDirPath, toolIdStr) + err = os.Mkdir(toolOutputDirPath, commonUtil.DefaultFileCreatePermission) + if err != nil && !os.IsExist(err) { + impl.Logger.Errorw("error in creating toolOutput directory", "toolId", tool.Id, "executionHistoryDir", executionHistoryDirPath, "err", err) + return err + } + //getting all steps for this tool + steps, err := impl.ScanToolStepRepository.FindAllByScanToolId(tool.Id) + if err != nil { + impl.Logger.Errorw("error in getting steps by scan tool id", "toolId", tool.Id, "err", err) + return err + } + //sorting steps on the basis of index + //sort.Slice(steps, func(i, j int) bool { return steps[i].Index < steps[j].Index }) + stepIndexMap := make(map[int]repository.ScanToolStep) + stepTryCount := make(map[int]int) //map of stepIndex and it's try count + var stepProcessIndex int + + // Getting and Setting the starting index based of first step for processing starting point on registry type and tool + registryIndexMappingModel, err := impl.RegistryIndexMappingRepository.GetStartingIndexForARegistryAndATool(tool.Id, imageScanRenderDto.RegistryType) + if err != nil { + impl.Logger.Errorw("error in getting registry index mapping", "RegistryType", imageScanRenderDto.RegistryType, "toolId", tool.Id, "err", err) + return err + } + stepProcessIndex = registryIndexMappingModel.Index + + for _, step := range steps { + stepCopy := *step + if stepCopy.Index == stepCopy.ExecuteStepOnFail { + stepTryCount[stepCopy.Index] = 1 + stepCopy.RetryCount // adding 1 for the initial try + } else { + stepTryCount[stepCopy.Index] = 1 // setting as 1 since only 1 try is needed + } + stepIndexMap[stepCopy.Index] = stepCopy + } + for { + if stepTryCount[stepProcessIndex] <= 0 { + return fmt.Errorf("error in completing tool scan process, max no of tries reached for failed step with index : %d", stepProcessIndex) + } + step := stepIndexMap[stepProcessIndex] + //decrementing try count for this step + stepTryCount[stepProcessIndex] -= 1 + if step.StepExecutionSync { + output, err := impl.ProcessScanStep(step, tool, toolOutputDirPath, ctx, imageScanRenderDto) + if err != nil { + impl.Logger.Errorw("error in processing scan step sync", "stepId", step.Id, "err", err) + return err + } + if step.StepExecutionType == bean.ScanExecutionTypeCli && step.CliOutputType == cliUtil.CliOutPutTypeStream { + // read output here for further processing, to update this logic when cli stream processing is made async + outputFileName := path.Join(toolOutputDirPath, fmt.Sprintf("%d%s", step.Index, bean.JsonOutputFileNameSuffix)) + output, err = commonUtil.ReadFile(outputFileName) + if err != nil { + impl.Logger.Errorw("error in getting reading output of step", "stepOutputFileName", outputFileName, "err", err) + return err + } + } + + isPassed, err := impl.CheckConditionsForAStep(step, output) + if err != nil { + impl.Logger.Errorw("error in checking conditions for step", "stepId", step.Id, "err", err) + return err + } + if !isPassed { + impl.Logger.Infow("conditions not passed for step", "stepId", step.Id) + return fmt.Errorf("conditions not passed for step with index : %d", step.Index) + } + if step.ExecuteStepOnPass == bean.NullProcessIndex && isPassed { //step process is passed and scanning is completed + err = impl.ConvertEndStepOutputAndSaveVulnerabilities(output, executionHistoryId, tool, step, userId) + if err != nil { + impl.Logger.Errorw("error in saving vulnerabilities", "err", err) + return err + } + return nil + } else if step.ExecuteStepOnFail == bean.NullProcessIndex && !isPassed { //step process is failed and scanning is completed + return fmt.Errorf("error in executing step with index : %d", stepProcessIndex) + } else if isPassed { //step process is passed and have to move to next step for processing + stepProcessIndex = step.ExecuteStepOnPass + } else if !isPassed { //step process is failed and have to move to next step for processing + stepProcessIndex = step.ExecuteStepOnFail //this step can be equal to the same step in case of retry or can be other one + } + } else { //async type processing + cxtx, cancel := context.WithTimeout(ctx, time.Duration(imageScanConfig.ScanImageAsyncTimeout)*time.Minute) + defer cancel() + go func() { + //will not check if step is passed or failed + _, err := impl.ProcessScanStep(step, tool, toolOutputDirPath, cxtx, nil) + if err != nil { + impl.Logger.Errorw("error in processing scan step async", "stepId", step.Id, "err", err) + return + } + }() + stepProcessIndex = step.ExecuteStepOnPass // for async type process, always considering step to be passed + if stepProcessIndex == bean.NullProcessIndex { // if end step, consider it completed and return + return nil + } + } + } +} + +func (impl *ImageScanServiceImpl) ProcessScanStep(step repository.ScanToolStep, tool repository.ScanToolMetadata, toolOutputDirPath string, ctx context.Context, imageScanRenderDto *common.ImageScanRenderDto) ([]byte, error) { + outputFileNameForThisStep := path.Join(toolOutputDirPath, fmt.Sprintf("%d%s", step.Index, bean.JsonOutputFileNameSuffix)) + var output []byte + if step.StepExecutionType == bean.ScanExecutionTypeHttp { + queryParams, httpHeaders, inputPayload, err := impl.GetHttpStepInputParams(step, toolOutputDirPath, nil) + if err != nil { + impl.Logger.Errorw("error in getting http step input params", "err", err) + return nil, err + } + output, err = httpUtil.HandleHTTPRequest(tool.ServerBaseUrl, step.HttpMethodType, httpHeaders, queryParams, inputPayload, outputFileNameForThisStep, ctx) + if err != nil { + impl.Logger.Errorw("error in http request txn", "err", err) + return nil, err + } + } else if step.StepExecutionType == bean.ScanExecutionTypeCli { + imageScanRenderDto.OutputFilePath = outputFileNameForThisStep + renderedCommand, err := impl.GetCliInputParams(step, toolOutputDirPath, imageScanRenderDto, tool.ToolMetaData) + if err != nil { + impl.Logger.Errorw("error in getting cli step input params", "err", err) + return nil, err + } + cliCommandEnv := impl.CliCommandEnv + output, err = cliUtil.HandleCliRequest(renderedCommand, outputFileNameForThisStep, ctx, step.CliOutputType, nil, cliCommandEnv) + if err != nil { + impl.Logger.Errorw("error in cli request txn", "err", err) + return nil, err + } + } + return output, nil +} + +func (impl *ImageScanServiceImpl) ConvertEndStepOutputAndSaveVulnerabilities(stepOutput []byte, executionHistoryId int, tool repository.ScanToolMetadata, step repository.ScanToolStep, userId int32) error { + var vulnerabilities []*bean.ImageScanOutputObject + var err error + if isV1Template(tool.ResultDescriptorTemplate) { // result descriptor template is go template, go with v1 logic + vulnerabilities, err = impl.getImageScanOutputObjectsV1(stepOutput, tool.ResultDescriptorTemplate) + if err != nil { + impl.Logger.Errorw("error, getImageScanOutputObjectsV1", "stepOutput", stepOutput, "resultDescriptorTemplate", tool.ResultDescriptorTemplate, "err", err) + return err + } + } else { //not go template, go with v2 logic + vulnerabilities, err = impl.getImageScanOutputObjectsV2(stepOutput, tool.ResultDescriptorTemplate) + if err != nil { + impl.Logger.Errorw("error, getImageScanOutputObjectsV2", "stepOutput", stepOutput, "resultDescriptorTemplate", tool.ResultDescriptorTemplate, "err", err) + return err + } + } + + cvesToBeSaved := make([]*repository.CveStore, 0, len(vulnerabilities)) + uniqueVulnerabilityMap := make(map[string]*bean.ImageScanOutputObject) + allCvesNames := make([]string, 0, len(vulnerabilities)) + for _, vul := range vulnerabilities { + if _, ok := uniqueVulnerabilityMap[vul.Name]; !ok { + uniqueVulnerabilityMap[vul.Name] = vul + allCvesNames = append(allCvesNames, vul.Name) + } + } + allSavedCvesMap := make(map[string]*repository.CveStore) + if len(allCvesNames) > 0 { + allSavedCves, err := impl.CveStoreRepository.FindByCveNames(allCvesNames) + if err != nil { + if err == pg.ErrNoRows { + // in case of no cve found , just ignore + impl.Logger.Infow("no saved cves found", err) + } else { + impl.Logger.Errorw("error in getting all cves ", "err", err) + return err + } + } + + for _, cve := range allSavedCves { + allSavedCvesMap[cve.Name] = cve + } + } + cvesToUpdate := make([]*repository.CveStore, 0, len(uniqueVulnerabilityMap)) + for _, vul := range uniqueVulnerabilityMap { + if val, ok := allSavedCvesMap[vul.Name]; ok { + // updating cve here if vulnerability has a new severity + vulnerabilityStandardSeverity := bean.StandardSeverityStringToEnum(bean.ConvertToLowerCase(vul.Severity)) + if vulnerabilityStandardSeverity != val.GetSeverity() { + val.UpdateNewSeverityInCveStore(vul.Severity, userId) + cvesToUpdate = append(cvesToUpdate, val) + } + } else { + cve := createCveStoreObject(vul.Name, vul.PackageVersion, vul.FixedInVersion, vul.Severity, userId) + cvesToBeSaved = append(cvesToBeSaved, cve) + } + } + + imageScanExecutionResults := make([]*repository.ImageScanExecutionResult, 0, len(vulnerabilities)) + for _, vul := range vulnerabilities { + imageScanExecutionResult := createImageScanExecutionResultObject(executionHistoryId, vul.Name, vul.Package, vul.PackageVersion, vul.FixedInVersion, vul.Class, vul.Type, vul.TargetName, tool.Id) + imageScanExecutionResults = append(imageScanExecutionResults, imageScanExecutionResult) + } + tx, err := impl.CveStoreRepository.GetConnection().Begin() + if err != nil { + impl.Logger.Errorw("error in initiating db transaction", "err", err) + return err + } + // Rollback tx on error. + defer tx.Rollback() + if len(cvesToBeSaved) > 0 { + err = impl.CveStoreRepository.SaveInBatch(cvesToBeSaved, tx) + if err != nil { + impl.Logger.Errorw("error in saving cves in batch", "err", err) + return err + } + } + if len(imageScanExecutionResults) > 0 { + err = impl.ScanResultRepository.SaveInBatch(imageScanExecutionResults, tx) + if err != nil { + impl.Logger.Errorw("error in saving scan execution results in batch", "err", err) + return err + } + } + if len(cvesToUpdate) > 0 { + _, err := impl.CveStoreRepository.UpdateInBatch(cvesToUpdate, tx) + if err != nil { + impl.Logger.Errorw("Failed to updateCveStoreWithUpdatedValues in batch", "err", err) + return err + } + } + err = tx.Commit() + if err != nil { + impl.Logger.Errorw("error in committing transaction", "err", err) + return err + } + return nil +} + +func isV1Template(resultDescriptorTemplate string) bool { + var mappings []map[string]interface{} + err := json.Unmarshal([]byte(resultDescriptorTemplate), &mappings) + return err != nil && isValidGoTemplate(resultDescriptorTemplate) //checking error too because our new result descriptor template can pass go templating too as it contains a simple json +} + +func isValidGoTemplate(templateStr string) bool { + _, err := template.New("test").Funcs(template.FuncMap{ //for trivy we use add function, so it needs to be defined here + "add": func(a, b int) int { return a + b }, + }).Parse(templateStr) + return err == nil +} + +func (impl *ImageScanServiceImpl) getImageScanOutputObjectsV1(stepOutput []byte, resultDescriptorTemplate string) ([]*bean.ImageScanOutputObject, error) { + //rendering image descriptor template with output json to get vulnerabilities updated + renderedTemplate, err := commonUtil.ParseJsonTemplate(resultDescriptorTemplate, stepOutput) + if err != nil { + impl.Logger.Errorw("error in parsing template to get vulnerabilities", "err", err) + return nil, err + } + renderedTemplate = common.RemoveTrailingComma(renderedTemplate) + var vulnerabilities []*bean.ImageScanOutputObject + err = json.Unmarshal([]byte(renderedTemplate), &vulnerabilities) + if err != nil { + impl.Logger.Errorw("error in unmarshalling rendered template", "err", err) + return nil, err + } + return vulnerabilities, nil +} + +func (impl *ImageScanServiceImpl) getImageScanOutputObjectsV2(stepOutput []byte, resultDescriptorTemplate string) ([]*bean.ImageScanOutputObject, error) { + var vulnerabilities []*bean.ImageScanOutputObject + var mappings []map[string]interface{} + err := json.Unmarshal([]byte(resultDescriptorTemplate), &mappings) + if err != nil { + impl.Logger.Errorw("error in un-marshaling result descriptor template", "resultDescriptorTemplate", resultDescriptorTemplate, "err", err) + return nil, err + } + var processArray func(mapping map[string]interface{}, value gjson.Result) + processArray = func(mapping map[string]interface{}, value gjson.Result) { + vulnerabilitiesPath := mapping[bean.MappingKeyPathToVulnerabilitiesArray].(string) + vulnerabilityDataKeyPathsMap := mapping[bean.MappingKeyPathToVulnerabilityDataKeys].(map[string]interface{}) + resultDataKeyPathsMap := mapping[bean.MappingKeyPathToResultDataKeys].(map[string]interface{}) + + value.ForEach(func(_, nestedValue gjson.Result) bool { + targetName, class, resType := "", "", "" + if nestedValue.IsObject() { + targetName, class, resType = nestedValue.Get(resultDataKeyPathsMap[bean.MappingTarget].(string)).String(), nestedValue.Get(resultDataKeyPathsMap[bean.MappingClass].(string)).String(), nestedValue.Get(resultDataKeyPathsMap[bean.MappingType].(string)).String() + + if nestedValue.Get(vulnerabilitiesPath).IsArray() { + nestedValue.Get(vulnerabilitiesPath).ForEach(func(_, vul gjson.Result) bool { + vulnerability := &bean.ImageScanOutputObject{ + Name: vul.Get(vulnerabilityDataKeyPathsMap[bean.MappingKeyName].(string)).String(), + Package: vul.Get(vulnerabilityDataKeyPathsMap[bean.MappingKeyPackage].(string)).String(), + PackageVersion: vul.Get(vulnerabilityDataKeyPathsMap[bean.MappingKeyPackageVersion].(string)).String(), + FixedInVersion: vul.Get(vulnerabilityDataKeyPathsMap[bean.MappingKeyFixedInVersion].(string)).String(), + Severity: vul.Get(vulnerabilityDataKeyPathsMap[bean.MappingKeySeverity].(string)).String(), + TargetName: targetName, + Class: class, + Type: resType, + } + vulnerabilities = append(vulnerabilities, vulnerability) + return true + }) + } + } + return true + }) + } + + for _, mapping := range mappings { + result := gjson.Get(string(stepOutput), mapping[bean.MappingKeyPathToResultsArray].(string)) + if !result.Exists() { + continue + } + processArray(mapping, result) + } + + return vulnerabilities, nil +} + +func (impl *ImageScanServiceImpl) GetHttpStepInputParams(step repository.ScanToolStep, toolOutputDirPath string, imageScanRenderDto *common.ImageScanRenderDto) (url.Values, map[string]string, *bytes.Buffer, error) { + var err error + var queryParams url.Values + httpHeaders := make(map[string]string) + inputPayload := &bytes.Buffer{} + inputPayloadBytes := step.HttpInputPayload + if step.HttpQueryParams != nil { + err = json.Unmarshal(step.HttpQueryParams, &queryParams) + if err != nil { + impl.Logger.Errorw("error in unmarshalling query params", "err", err) + return queryParams, httpHeaders, inputPayload, err + } + } + if step.HttpReqHeaders != nil { + err = json.Unmarshal(step.HttpReqHeaders, &httpHeaders) + if err != nil { + impl.Logger.Errorw("error in unmarshalling http headers", "err", err) + return queryParams, httpHeaders, inputPayload, err + } + } + inputPayloadBytes, err = impl.RenderInputDataForAStep(string(step.HttpInputPayload), step.RenderInputDataFromStep, toolOutputDirPath, imageScanRenderDto, "") + if err != nil { + impl.Logger.Errorw("error in rendering http input payload", "err", err) + return queryParams, httpHeaders, inputPayload, err + } + inputPayload = bytes.NewBuffer(inputPayloadBytes) + return queryParams, httpHeaders, inputPayload, nil +} + +func (impl *ImageScanServiceImpl) GetCliInputParams(step repository.ScanToolStep, toolOutputDirPath string, imageScanRenderDto *common.ImageScanRenderDto, toolMetaData string) (string, error) { + var err error + var renderedCommand []byte + renderedCommand, err = impl.RenderInputDataForAStep(step.CliCommand, step.RenderInputDataFromStep, toolOutputDirPath, imageScanRenderDto, toolMetaData) + if err != nil { + impl.Logger.Errorw("error in rendering cli input args", "err", err) + return "", err + } + return string(renderedCommand), nil +} + +func (impl *ImageScanServiceImpl) RenderInputDataForAStep(inputPayloadTmpl string, outputStepIndex int, toolExecutionDirectoryPath string, imageScanRenderDto *common.ImageScanRenderDto, toolMetaData string) ([]byte, error) { + tmpl := template.Must(template.New("").Parse(inputPayloadTmpl)) + jsonMap := map[string]interface{}{} + metaDataMap := map[string]interface{}{} + err := json.Unmarshal([]byte(toolMetaData), &metaDataMap) + if err != nil { + impl.Logger.Errorw("error in unmarshalling meta data ", "toolMetaData", toolMetaData, "err", err) + return nil, err + } + if outputStepIndex != bean.NullProcessIndex { + outputFileName := path.Join(toolExecutionDirectoryPath, fmt.Sprintf("%d%s", outputStepIndex, bean.JsonOutputFileNameSuffix)) + outputFromStep, err := commonUtil.ReadFile(outputFileName) + if err != nil { + impl.Logger.Errorw("error in getting reading output of step", "stepOutputFileName", outputFromStep, "err", err) + return nil, err + } + err = json.Unmarshal(outputFromStep, &jsonMap) + if err != nil { + impl.Logger.Errorw("error in unmarshalling", "err", err) + return nil, err + } + } + //entering imageScanRenderData in above json map; TODO: update this to some other logic to handle more fields in future + jsonMap[common.AWSSecretAccessKey] = imageScanRenderDto.AWSSecretAccessKey + jsonMap[common.AWSAccessKeyId] = imageScanRenderDto.AWSAccessKeyId + jsonMap[common.AWSRegion] = imageScanRenderDto.AWSRegion + jsonMap[common.Username] = imageScanRenderDto.Username + jsonMap[common.Password] = imageScanRenderDto.Password + jsonMap[common.GCR_FILE_PATH] = toolExecutionDirectoryPath + jsonMap[common.IMAGE_NAME] = imageScanRenderDto.Image + jsonMap[common.OUTPUT_FILE_PATH] = imageScanRenderDto.OutputFilePath + jsonMap[common.EXTRA_ARGS] = "" + jsonMap[common.CA_CERT_FILE_PATH] = imageScanRenderDto.CaCertFilePath + jsonMap[common.INSECURE] = imageScanRenderDto.DockerConnection == common.INSECURE + + for key, val := range metaDataMap { + jsonMap[key] = val + } + buf := &bytes.Buffer{} + err = tmpl.Execute(buf, jsonMap) + if err != nil { + impl.Logger.Errorw("error in executing template", "err", err) + return nil, err + } + return buf.Bytes(), nil +} + +func (impl *ImageScanServiceImpl) CreateScanExecutionRegistryForClairV4(vs []*claircore.Vulnerability, event *common.ImageScanEvent, toolId int, executionHistory *repository.ImageScanExecutionHistory) ([]*claircore.Vulnerability, error) { + + imageScanExecutionResultsToBeSaved := make([]*repository.ImageScanExecutionResult, 0, len(vs)) + cvesToUpdate := make([]*repository.CveStore, 0, len(vs)) + cvesToBeSaved := make([]*repository.CveStore, 0, len(vs)) + userId := int32(event.UserId) + for _, item := range vs { + cveStore, err := impl.CveStoreRepository.FindByName(item.Name) + if err != nil && err != pg.ErrNoRows { + impl.Logger.Errorw("Failed to fetch cve", "err", err) + return nil, err + } + if len(cveStore.Name) == 0 { + cveStore = createCveStoreObject(item.Name, item.Package.Version, item.FixedInVersion, item.Severity, userId) + cvesToBeSaved = append(cvesToBeSaved, cveStore) + } else { + // updating cve here if vulnerability has a new severity + vulnerabilityStandardSeverity := bean.StandardSeverityStringToEnum(bean.ConvertToLowerCase(item.Severity)) + if vulnerabilityStandardSeverity != cveStore.GetSeverity() { + cveStore.UpdateNewSeverityInCveStore(item.Severity, userId) + cvesToUpdate = append(cvesToUpdate, cveStore) + } + } + imageScanExecutionResult := createImageScanExecutionResultObject(executionHistory.Id, item.Name, item.Package.Name, item.Package.Version, item.FixedInVersion, "", "", "", toolId) + imageScanExecutionResultsToBeSaved = append(imageScanExecutionResultsToBeSaved, imageScanExecutionResult) + } + tx, err := impl.CveStoreRepository.GetConnection().Begin() + if err != nil { + impl.Logger.Errorw("error in initiating db transaction", "err", err) + return nil, err + } + // Rollback tx on error. + defer tx.Rollback() + if len(cvesToBeSaved) > 0 { + err = impl.CveStoreRepository.SaveInBatch(cvesToBeSaved, tx) + if err != nil { + impl.Logger.Errorw("error in saving cves in batch", "err", err) + return nil, err + } + } + if len(imageScanExecutionResultsToBeSaved) > 0 { + err = impl.ScanResultRepository.SaveInBatch(imageScanExecutionResultsToBeSaved, tx) + if err != nil { + impl.Logger.Errorw("error in saving scan execution results in batch", "err", err) + return nil, err + } + } + if len(cvesToUpdate) > 0 { + _, err = impl.CveStoreRepository.UpdateInBatch(cvesToUpdate, tx) + if err != nil { + impl.Logger.Errorw("error in updating cves in batch", "err", err) + return nil, err + } + } + err = tx.Commit() + if err != nil { + impl.Logger.Errorw("error in committing transaction", "err", err) + return nil, err + } + return vs, nil +} + +func (impl *ImageScanServiceImpl) CreateScanExecutionRegistryForClairV2(vs []*clair.Vulnerability, event *common.ImageScanEvent, toolId int, executionHistory *repository.ImageScanExecutionHistory) ([]*clair.Vulnerability, error) { + + imageScanExecutionResultsToBeSaved := make([]*repository.ImageScanExecutionResult, 0, len(vs)) + cvesToUpdate := make([]*repository.CveStore, 0, len(vs)) + cvesToBeSaved := make([]*repository.CveStore, 0, len(vs)) + userId := int32(event.UserId) + for _, item := range vs { + cveStore, err := impl.CveStoreRepository.FindByName(item.Name) + if err != nil && err != pg.ErrNoRows { + impl.Logger.Errorw("Failed to fetch cve", "err", err) + return nil, err + } + if len(cveStore.Name) == 0 { + cveStore = createCveStoreObject(item.Name, item.FeatureVersion, item.FixedBy, item.Severity, userId) + cvesToBeSaved = append(cvesToBeSaved, cveStore) + } else { + // updating cve here if vulnerability has a new severity + vulnerabilityStandardSeverity := bean.StandardSeverityStringToEnum(bean.ConvertToLowerCase(item.Severity)) + if vulnerabilityStandardSeverity != cveStore.GetSeverity() { + cveStore.UpdateNewSeverityInCveStore(item.Severity, userId) + cvesToUpdate = append(cvesToUpdate, cveStore) + } + } + imageScanExecutionResult := createImageScanExecutionResultObject(executionHistory.Id, item.Name, item.FeatureName, item.FeatureVersion, item.FixedBy, "", "", "", toolId) + imageScanExecutionResultsToBeSaved = append(imageScanExecutionResultsToBeSaved, imageScanExecutionResult) + } + tx, err := impl.CveStoreRepository.GetConnection().Begin() + if err != nil { + impl.Logger.Errorw("error in initiating db transaction", "err", err) + return nil, err + } + // Rollback tx on error. + defer tx.Rollback() + if len(cvesToBeSaved) > 0 { + err = impl.CveStoreRepository.SaveInBatch(cvesToBeSaved, tx) + if err != nil { + impl.Logger.Errorw("error in saving cves in batch", "err", err) + return nil, err + } + } + if len(imageScanExecutionResultsToBeSaved) > 0 { + err = impl.ScanResultRepository.SaveInBatch(imageScanExecutionResultsToBeSaved, tx) + if err != nil { + impl.Logger.Errorw("error in saving scan execution results in batch", "err", err) + return nil, err + } + } + if len(cvesToUpdate) > 0 { + _, err = impl.CveStoreRepository.UpdateInBatch(cvesToUpdate, tx) + if err != nil { + impl.Logger.Errorw("error in updating cves in batch", "err", err) + return nil, err + } + } + err = tx.Commit() + if err != nil { + impl.Logger.Errorw("error in committing transaction", "err", err) + return nil, err + } + return vs, nil +} + +func (impl *ImageScanServiceImpl) IsImageScanned(image string, hasSource bool) (int, bool, error) { + scanned := false + scanHistory, err := impl.ScanHistoryRepository.FindByImage(image) + if err != nil && err != pg.ErrNoRows { + impl.Logger.Errorw("error in fetching scan history ", "err", err) + return 0, scanned, err + } + scanHistoryId := 0 + if scanHistory != nil { + scanHistoryId = scanHistory.Id + //scanned = true + } + if scanHistoryId > 0 { + scanHistoryMappings, err := impl.ScanToolExecutionHistoryMappingRepository.GetAllScanHistoriesByExecutionHistoryIdAndStates(scanHistoryId, []bean.ScanExecutionProcessState{bean.ScanExecutionProcessStateRunning, bean.ScanExecutionProcessStateCompleted}) + if err != nil && err != pg.ErrNoRows { + impl.Logger.Errorw("error in getting history mappings", "err", err) + return 0, scanned, err + } + if len(scanHistoryMappings) > 0 { + scanned = true + } + } + + return 0, scanned, err +} + +func (impl *ImageScanServiceImpl) CheckConditionsForAStep(step repository.ScanToolStep, stepOutput []byte) (bool, error) { + //get all conditions for a step + conditions, err := impl.ScanStepConditionRepository.FindAllByToolStepId(step.Id) + if err != nil { + impl.Logger.Errorw("error in getting all conditions by step id", "stepId", step.Id, "err", err) + return false, err + } + for _, condition := range conditions { + isPassedForCondition, err := impl.EvaluateCondition(*condition, stepOutput) + if err != nil { + impl.Logger.Errorw("error in evaluating condition", "condition", condition, "err", err) + return false, err + } + if !isPassedForCondition { //condition failed, will not check further + return false, nil + } + } + return true, nil +} + +func (impl *ImageScanServiceImpl) EvaluateCondition(condition repository.ScanStepCondition, stepOutput []byte) (bool, error) { + expression, err := govaluate.NewEvaluableExpression(fmt.Sprintf("conditionOn %s conditionalVal", condition.ConditionalOperator)) + if err != nil { + return false, err + } + conditionOnRaw := gjson.Get(string(stepOutput), condition.ConditionOn).String() + conditionOn, err := bean.ConvertVariableFormat(conditionOnRaw, condition.ConditionVariableFormat) + if err != nil { + return false, err + } + conditionVal, err := bean.ConvertVariableFormat(condition.ConditionalValue, condition.ConditionVariableFormat) + if err != nil { + return false, err + } + parameters := make(map[string]interface{}, 2) + parameters["conditionOn"] = conditionOn + parameters["conditionalVal"] = conditionVal + evaluation, err := expression.Evaluate(parameters) + if err != nil { + return false, err + } + isPassed := evaluation.(bool) + return isPassed, nil +} + +func (impl *ImageScanServiceImpl) HandleProgressingScans() { + //marking all scans failed which have crossed try count + err := impl.ScanToolExecutionHistoryMappingRepository.MarkAllRunningStateAsFailedHavingTryCountReachedLimit(impl.ImageScanConfig.ScanTryCount) + if err != nil { + impl.Logger.Errorw("error in marking all running scan states as failed", "err", err) + return + } + + //getting all scans which are in progressing after marking failed + scanHistories, err := impl.ScanToolExecutionHistoryMappingRepository.GetAllScanHistoriesByState(bean.ScanExecutionProcessStateRunning) + if err != nil { + impl.Logger.Errorw("error in getting all scans by running state", "err", err) + return + } + + var executionHistoryDirPath string + flagForDeleting := false + // Create Folder for output data for execution history only if any pending scans are there due to pod died + if len(scanHistories) > 0 { + flagForDeleting = true + executionHistoryDirPath = impl.CreateFolderForOutputData(scanHistories[0].ImageScanExecutionHistoryId) + } + wg := &sync.WaitGroup{} + wg.Add(len(scanHistories)) + imagescanExecutionHistories, err := impl.ScanHistoryRepository.FindAll() + if err != nil { + impl.Logger.Errorw("error in getting scan histories on start up", "err", err) + return + } + imageScanToolMetadatas, err := impl.ScanToolMetadataRepository.FindAllActiveTools() + if err != nil { + impl.Logger.Errorw("error in getting all active tools", "err", err) + } + imageScanExecutionHistoryMap := make(map[int]*repository.ImageScanExecutionHistory) + imageScanToolsMap := make(map[int]*repository.ScanToolMetadata) + + for _, imageScanExecutionHistory := range imagescanExecutionHistories { + imageScanExecutionHistoryMap[imageScanExecutionHistory.Id] = imageScanExecutionHistory + } + for _, imageScanToolMetaData := range imageScanToolMetadatas { + imageScanToolsMap[imageScanToolMetaData.Id] = imageScanToolMetaData + } + + //System doing image scanning for all pending scans + for _, scanHistory := range scanHistories { + scanEvent := common.ImageScanEvent{} + scanEventJson := imageScanExecutionHistoryMap[scanHistory.ImageScanExecutionHistoryId].SourceMetadataJson + if len(scanEventJson) == 0 { + return + } + scanTool := imageScanToolsMap[scanHistory.ScanToolId] + err = json.Unmarshal([]byte(scanEventJson), &scanEvent) + if err != nil { + impl.Logger.Errorw("error in un-marshaling", "err", err) + return + } + imageScanRenderDto, err := impl.GetImageScanRenderDto(scanEvent.DockerRegistryId, &scanEvent) + if err != nil { + impl.Logger.Errorw("service error, GetImageScanRenderDto", "dockerRegistryId", scanEvent.DockerRegistryId, "err", err) + return + } + _, _, err = impl.ScanImageForTool(scanTool, scanHistory.ImageScanExecutionHistoryId, executionHistoryDirPath, wg, 1, context.Background(), imageScanRenderDto, false) + if err != nil { + impl.Logger.Errorw("error in scanning image", "err", err) + return + } + } + wg.Wait() + + //deleting executionDirectoryPath + if flagForDeleting { + err = os.Remove(executionHistoryDirPath) + if err != nil { + impl.Logger.Errorw("error in deleting executionHistoryDirectory", "executionHistoryDirPath", executionHistoryDirPath, "err", err) + } + } + +} + +func (impl *ImageScanServiceImpl) FetchProxyUrl(scanEvent *common.ImageScanEvent) (string, []name.Option, error) { + return "", []name.Option{}, nil +} + +func GetImageScannerConfig() (*ImageScanConfig, error) { + scannerConfig := &ImageScanConfig{} + err := env.Parse(scannerConfig) + if err != nil { + return nil, errors.New(fmt.Sprintf("could not get scanner config from environment :%v", err)) + } + return scannerConfig, err +} + +type ImageScanConfig struct { + ScannerType string `env:"SCANNER_TYPE" envDefault:""` + ScanTryCount int `env:"IMAGE_SCAN_TRY_COUNT" envDefault:"1"` + ScanImageTimeout int `env:"IMAGE_SCAN_TIMEOUT" envDefault:"10"` // Time is considered in minutes + ScanImageAsyncTimeout int `env:"IMAGE_SCAN_ASYNC_TIMEOUT" envDefault:"3"` // Time is considered in minutes +} diff --git a/image-scanner/pkg/security/bean.go b/image-scanner/pkg/security/bean.go new file mode 100644 index 000000000..bf50be010 --- /dev/null +++ b/image-scanner/pkg/security/bean.go @@ -0,0 +1,13 @@ +package security + +import ( + "github.com/devtron-labs/image-scanner/common" + "github.com/devtron-labs/image-scanner/pkg/sql/repository" +) + +type ScanCodeRequest struct { + ScanEvent *common.ImageScanEvent + Tool *repository.ScanToolMetadata + ExecutionHistory *repository.ImageScanExecutionHistory + ExecutionHistoryDirPath string +} diff --git a/image-scanner/pkg/security/helper.go b/image-scanner/pkg/security/helper.go new file mode 100644 index 000000000..801c029ce --- /dev/null +++ b/image-scanner/pkg/security/helper.go @@ -0,0 +1,33 @@ +package security + +import ( + "github.com/devtron-labs/image-scanner/pkg/sql/bean" + "github.com/devtron-labs/image-scanner/pkg/sql/repository" +) + +func createCveStoreObject(name, version, fixedInVersion, severity string, userId int32) *repository.CveStore { + cveStore := &repository.CveStore{ + Name: name, + Version: version, + FixedVersion: fixedInVersion, + } + lowerCaseSeverity := bean.ConvertToLowerCase(severity) + cveStore.Severity = bean.SeverityStringToEnum(lowerCaseSeverity) + cveStore.SetStandardSeverity(bean.StandardSeverityStringToEnum(lowerCaseSeverity)) + cveStore.CreateAuditLog(userId) + return cveStore +} + +func createImageScanExecutionResultObject(executionHistoryId int, vulName, packageName, version, fixedInVersion, className, typeName, targetName string, toolId int) *repository.ImageScanExecutionResult { + return &repository.ImageScanExecutionResult{ + ImageScanExecutionHistoryId: executionHistoryId, + CveStoreName: vulName, + Package: packageName, + ScanToolId: toolId, + Version: version, + FixedVersion: fixedInVersion, + Target: targetName, + Type: typeName, + Class: className, + } +} diff --git a/image-scanner/pkg/security/imageScanService_test.go b/image-scanner/pkg/security/imageScanService_test.go new file mode 100644 index 000000000..8ed9caada --- /dev/null +++ b/image-scanner/pkg/security/imageScanService_test.go @@ -0,0 +1,65 @@ +package security + +import ( + "fmt" + "github.com/devtron-labs/image-scanner/pkg/logger" + "github.com/tidwall/gjson" + "os" + "testing" +) + +const jsonKeys = "[ \n { \n \"pathToResultArray\": \"Results\",\n \"pathToVulnerabilitiesArray\": \"Vulnerabilities\",\n \"vulnerabilityData\":{\n \t\t\"name\": \"VulnerabilityID\",\n \t\"package\": \"PkgName\", \n \t\"packageVersion\": \"InstalledVersion\",\n \t\"fixedInVersion\": \"FixedVersion\",\n \t\"severity\": \"Severity\"\n },\n \"resultData\":{\n \t\t\t\"target\":\"Target\",\n \t\"class\":\"Class\",\n \t\"type\":\"Type\" \n }\n }\n]" + +func TestScan(t *testing.T) { + bytes, err := os.ReadFile("testTrivyScanResultV2.json") + if err != nil { + t.Fail() + } + + logger, _ := logger.InitLogger() + impl := ImageScanServiceImpl{Logger: logger} + out, err := impl.getImageScanOutputObjectsV2(bytes, jsonKeys) + fmt.Println(out, err) + if err != nil || len(out) == 0 { + t.Fail() + } + + cnt, err := countVuln(bytes) + if err != nil { + t.Fail() + } + + if cnt != len(out) { + t.Fail() + } +} + +func countVuln(jsonStr []byte) (int, error) { + result := gjson.Get(string(jsonStr), "Results") + if !result.Exists() { + return 0, nil + } + + targetCountMap := make(map[string]int) + + result.ForEach(func(_, nestedValue gjson.Result) bool { + if nestedValue.IsObject() { + count := 0 + targetName := nestedValue.Get("Target").String() + if nestedValue.Get("Vulnerabilities").IsArray() { + nestedValue.Get("Vulnerabilities").ForEach(func(_, vul gjson.Result) bool { + count++ + return true + }) + } + targetCountMap[targetName] += count + } + return true + }) + + count := 0 + for _, cnt := range targetCountMap { + count += cnt + } + return count, nil +} diff --git a/image-scanner/pkg/sql/bean/bean.go b/image-scanner/pkg/sql/bean/bean.go new file mode 100644 index 000000000..ff89d2365 --- /dev/null +++ b/image-scanner/pkg/sql/bean/bean.go @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package bean + +import ( + "fmt" + "strconv" + "strings" +) + +const UserSystemId = 1 + +const ( + ScanOutputDirectory = "/security/devtronimagescan" // This is not configurable due to permissions for devtron-user + NullProcessIndex = -1 + JsonOutputFileNameSuffix = "_out.json" + ScannerTypeClairV4 = "CLAIRV4" + ScannerTypeClairV2 = "CLAIRV2" + ScannerTypeTrivy = "TRIVY" + ScanToolClair = "CLAIR" + ScanToolVersion2 = "V2" + ScanToolVersion4 = "V4" + SbomOutputFileNameSuffix = "_out.json" +) + +type ScanExecutionType string + +const ( + ScanExecutionTypeHttp ScanExecutionType = "HTTP" + ScanExecutionTypeCli ScanExecutionType = "CLI" +) + +type ScanExecutionProcessState int + +const ( + ScanExecutionProcessStateFailed ScanExecutionProcessState = iota - 1 //resolved value = -1 + ScanExecutionProcessStateRunning //resolved value = 0 + ScanExecutionProcessStateCompleted //resolved value = 1 +) + +type ImageScanOutputObject struct { + TargetName string `json:"targetName"` + Class string `json:"class"` + Type string `json:"type"` + Name string `json:"name"` + Package string `json:"package"` + PackageVersion string `json:"packageVersion"` + FixedInVersion string `json:"fixedInVersion"` + Severity string `json:"severity"` +} + +// Mapping is the used to store mappings of fields in ImageScanOutputObject and the path at which they are present in stepOutput +type Mapping map[string]string + +const ( + MappingKeyPathToResultDataKeys = "resultData" + MappingKeyPathToVulnerabilityDataKeys = "vulnerabilityData" + MappingKeyPathToResultsArray = "pathToResultArray" + MappingKeyPathToVulnerabilitiesArray = "pathToVulnerabilitiesArray" + MappingKeyName = "name" + MappingKeyPackage = "package" + MappingKeyPackageVersion = "packageVersion" + MappingKeyFixedInVersion = "fixedInVersion" + MappingKeySeverity = "severity" + MappingTarget = "target" + MappingType = "type" + MappingClass = "class" +) + +type Severity int + +const ( + HIGH string = "high" + CRITICAL string = "critical" + SAFE string = "safe" + LOW string = "low" + MEDIUM string = "medium" + MODERATE string = "moderate" + UNKNOWN string = "unknown" +) + +const ( + Low Severity = iota + Medium + Critical + High + Safe + Unknown +) + +func (sev Severity) String() string { + return [...]string{LOW, MEDIUM, CRITICAL, HIGH, SAFE, UNKNOWN}[sev] +} +func ConvertToLowerCase(input string) string { + return strings.ToLower(input) +} + +func SeverityStringToEnum(severity string) Severity { + if severity == LOW || severity == SAFE { + return Low + } else if severity == MEDIUM || severity == MODERATE { + return Medium + } else if severity == HIGH || severity == CRITICAL { + return Critical + } else if severity == UNKNOWN { + return Unknown + } + return Low +} + +func StandardSeverityStringToEnum(severity string) Severity { + if severity == LOW { + return Low + } else if severity == MEDIUM || severity == MODERATE { + return Medium + } else if severity == HIGH { + return High + } else if severity == CRITICAL { + return Critical + } else if severity == SAFE { + return Safe + } else if severity == UNKNOWN { + return Unknown + } + return Low +} + +type VariableFormat string + +const ( + VariableFormatString VariableFormat = "STRING" + VariableFormatBoolean VariableFormat = "BOOLEAN" + VariableFormatNumber VariableFormat = "NUMBER" +) + +func ConvertVariableFormat(value string, varFormat VariableFormat) (interface{}, error) { + switch varFormat { + case VariableFormatString: + return value, nil + case VariableFormatNumber: + return strconv.ParseFloat(value, 8) + case VariableFormatBoolean: + return strconv.ParseBool(value) + default: + return nil, fmt.Errorf("format not supported") + } +} + +type UserInfo struct { + Id int32 `json:"id" validate:"number"` + EmailId string `json:"email_id" validate:"required"` + Roles []string `json:"roles,omitempty"` + AccessToken string `json:"access_token,omitempty"` + Exist bool `json:"-"` + UserId int32 `json:"-"` // created or modified user id + Status string `json:"status,omitempty"` + Groups []string `json:"groups"` + SuperAdmin bool `json:"superAdmin,notnull"` +} diff --git a/image-scanner/pkg/sql/connection.go b/image-scanner/pkg/sql/connection.go new file mode 100644 index 000000000..0f3765a46 --- /dev/null +++ b/image-scanner/pkg/sql/connection.go @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sql + +import ( + "reflect" + "time" + + "github.com/caarlos0/env/v6" + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type Config struct { + Addr string `env:"PG_ADDR" envDefault:"127.0.0.1"` + Port string `env:"PG_PORT" envDefault:"5432"` + User string `env:"PG_USER" envDefault:""` + Password string `env:"PG_PASSWORD" envDefault:"" secretData:"-"` + Database string `env:"PG_DATABASE" envDefault:"orchestrator"` + ApplicationName string `env:"APP" envDefault:"image-scanner"` + LogQuery bool `env:"PG_LOG_QUERY" envDefault:"true"` +} + +func GetConfig() (*Config, error) { + cfg := &Config{} + err := env.Parse(cfg) + return cfg, err +} + +func NewDbConnection(cfg *Config, logger *zap.SugaredLogger) (*pg.DB, error) { + options := pg.Options{ + Addr: cfg.Addr + ":" + cfg.Port, + User: cfg.User, + Password: cfg.Password, + Database: cfg.Database, + ApplicationName: cfg.ApplicationName, + } + dbConnection := pg.Connect(&options) + //check db connection + var test string + _, err := dbConnection.QueryOne(&test, `SELECT 1`) + + if err != nil { + logger.Errorw("error in connecting db ", "db", obfuscateSecretTags(cfg), "err", err) + return nil, err + } else { + logger.Infow("connected with db", "db", obfuscateSecretTags(cfg)) + } + //-------------- + if cfg.LogQuery { + dbConnection.OnQueryProcessed(func(event *pg.QueryProcessedEvent) { + query, err := event.FormattedQuery() + if err != nil { + panic(err) + } + logger.Infow("query time", + "duration", time.Since(event.StartTime), + "query", query) + }) + } + return dbConnection, err +} + +func obfuscateSecretTags(cfg interface{}) interface{} { + + cfgDpl := reflect.New(reflect.ValueOf(cfg).Elem().Type()).Interface() + cfgDplElm := reflect.ValueOf(cfgDpl).Elem() + t := cfgDplElm.Type() + for i := 0; i < t.NumField(); i++ { + if _, ok := t.Field(i).Tag.Lookup("secretData"); ok { + cfgDplElm.Field(i).SetString("********") + } else { + cfgDplElm.Field(i).Set(reflect.ValueOf(cfg).Elem().Field(i)) + } + } + return cfgDpl +} + +//TODO: call it from somewhere +/*func closeConnection() error { + return dbConnection.Close() +}*/ diff --git a/image-scanner/pkg/sql/repository/CiArtifactRepository.go b/image-scanner/pkg/sql/repository/CiArtifactRepository.go new file mode 100644 index 000000000..8f624ca80 --- /dev/null +++ b/image-scanner/pkg/sql/repository/CiArtifactRepository.go @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/go-pg/pg" + "go.uber.org/zap" + "time" +) + +type CiArtifact struct { + tableName struct{} `sql:"ci_artifact" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + PipelineId int `sql:"pipeline_id,notnull"` //id of the ci pipeline from which this webhook was triggered + Image string `sql:"image,notnull"` + ImageDigest string `sql:"image_digest,notnull"` + MaterialInfo string `sql:"material_info"` //git material metadata json array string + DataSource string `sql:"data_source,notnull"` + WorkflowId *int `sql:"ci_workflow_id"` + ParentCiArtifact int `sql:"parent_ci_artifact"` + ScanEnabled bool `sql:"scan_enabled"` + Scanned bool `sql:"scanned"` + DeployedTime time.Time `sql:"-"` + Deployed bool `sql:"-"` + Latest bool `sql:"-"` + AuditLog +} + +type CiArtifactRepository interface { + Update(artifact *CiArtifact) error + Get(imageDigest string) (*CiArtifact, error) +} + +type CiArtifactRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewCiArtifactRepositoryImpl(dbConnection *pg.DB, logger *zap.SugaredLogger) *CiArtifactRepositoryImpl { + return &CiArtifactRepositoryImpl{dbConnection: dbConnection, logger: logger} +} + +func (impl CiArtifactRepositoryImpl) Update(artifact *CiArtifact) error { + return impl.dbConnection.Update(artifact) +} + +func (impl CiArtifactRepositoryImpl) Get(imageDigest string) (*CiArtifact, error) { + var artifact CiArtifact + //TODO - rethink, is image hash is same for artifact id + err := impl.dbConnection.Model(&artifact).Where("image_digest = ?", imageDigest).Order("id desc").Limit(1).Select() + return &artifact, err +} diff --git a/image-scanner/pkg/sql/repository/CvePolicyControle.go b/image-scanner/pkg/sql/repository/CvePolicyControle.go new file mode 100644 index 000000000..cc2ae1f06 --- /dev/null +++ b/image-scanner/pkg/sql/repository/CvePolicyControle.go @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/devtron-labs/image-scanner/pkg/sql/bean" + "github.com/go-pg/pg" +) + +type CvePolicy struct { + tableName struct{} `sql:"cve_policy_control" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + Global bool + ClusterId int + EnvironmentId int + AppId int + CVEStoreId int + Action PolicyAction + Severity bean.Severity + Deleted bool + AuditLog + *CveStore +} + +type PolicyAction int + +const ( + Inherit PolicyAction = iota + Allow + Block +) + +func (d PolicyAction) String() string { + return [...]string{"Inherit", "Allow", "Block"}[d] +} + +type PolicyLevel int + +const ( + Global PolicyLevel = iota + Cluster + Environment + App +) + +func (d PolicyLevel) String() string { + return [...]string{"Global", "Cluster", "Environment", "App"}[d] +} + +func (policy *CvePolicy) PolicyLevel() PolicyLevel { + if policy.ClusterId != 0 { + return Cluster + } else if policy.EnvironmentId != 0 { + return Environment + } else if policy.AppId != 0 { + return App + } else { + return Global + } +} + +//------------------ + +type CvePolicyRepository interface { + GetEnvPolicies(clusterId int, environmentId int) (policies []*CvePolicy, err error) + GetAppEnvPolicies(clusterId int, environmentId int, appId int) (policies []*CvePolicy, err error) +} +type CvePolicyRepositoryImpl struct { + dbConnection *pg.DB +} + +func (impl *CvePolicyRepositoryImpl) GetEnvPolicies(clusterId int, environmentId int) (policies []*CvePolicy, err error) { + err = impl.dbConnection.Model(policies). + Where("global = true"). + Where("cluster_id= ?", clusterId). + Where("environmentId = ?", environmentId). + Where("deleted = false"). + Select() + return policies, err +} + +func (impl *CvePolicyRepositoryImpl) GetAppEnvPolicies(clusterId int, environmentId int, appId int) (policies []*CvePolicy, err error) { + err = impl.dbConnection.Model(policies). + Where("global = true"). + Where("cluster_id= ?", clusterId). + Where("environment_id = ?", environmentId). + Where("app_id = ?", appId). + Where("deleted = false"). + Select() + return policies, err +} diff --git a/image-scanner/pkg/sql/repository/CveStoreRepository.go b/image-scanner/pkg/sql/repository/CveStoreRepository.go new file mode 100644 index 000000000..54e3fcbd8 --- /dev/null +++ b/image-scanner/pkg/sql/repository/CveStoreRepository.go @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/devtron-labs/image-scanner/pkg/sql/bean" + "github.com/go-pg/pg" + "go.uber.org/zap" + "time" +) + +type CveStore struct { + tableName struct{} `sql:"cve_store" pg:",discard_unknown_columns"` + Name string `sql:"name,pk"` + + // Deprecated: Severity, use StandardSeverity for all read purposes + Severity bean.Severity `sql:"severity,notnull"` + // Deprecated: Package + Package string `sql:"package,notnull"` // deprecated, storing package data in image_scan_execution_result table + // Deprecated: Version + Version string `sql:"version,notnull"` + // Deprecated: FixedVersion + FixedVersion string `sql:"fixed_version,notnull"` + + // StandardSeverity is the actual severity. use GetSeverity method to get severity of the vulnerability + // earlier severity is maintained in Severity column by merging HIGH and CRITICAL severities. + // later we introduced new column StandardSeverity to store raw severity, but didn't migrate the existing Severity data to StandardSeverity. + // currently, we deprecated Severity. + StandardSeverity *bean.Severity `sql:"standard_severity"` + AuditLog +} + +// GetSeverity returns the actual severity of the vulnerability. +func (cve *CveStore) GetSeverity() bean.Severity { + if cve.StandardSeverity == nil { + // we need this as there was a time when StandardSeverity didn't exist. + // and migration of Severity data to StandardSeverity is not done. + return cve.Severity + } + return *cve.StandardSeverity +} + +func (cve *CveStore) CreateAuditLog(userId int32) { + cve.CreatedBy = userId + cve.CreatedOn = time.Now() + cve.UpdatedBy = userId + cve.UpdatedOn = time.Now() +} + +func (cve *CveStore) UpdateNewSeverityInCveStore(severity string, userId int32) { + lowerCaseSeverity := bean.ConvertToLowerCase(severity) + cve.Severity = bean.SeverityStringToEnum(lowerCaseSeverity) + cve.SetStandardSeverity(bean.StandardSeverityStringToEnum(lowerCaseSeverity)) + cve.UpdatedOn = time.Now() + cve.UpdatedBy = userId +} + +func (cve *CveStore) SetStandardSeverity(severity bean.Severity) { + cve.StandardSeverity = &severity +} + +type CveStoreRepository interface { + GetConnection() (dbConnection *pg.DB) + Save(model *CveStore) error + SaveInBatch(model []*CveStore, tx *pg.Tx) error + FindAll() ([]*CveStore, error) + FindByCveNames(names []string) ([]*CveStore, error) + FindByName(name string) (*CveStore, error) + Update(model *CveStore) error + UpdateInBatch(model []*CveStore, tx *pg.Tx) ([]*CveStore, error) +} + +type CveStoreRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewCveStoreRepositoryImpl(dbConnection *pg.DB, logger *zap.SugaredLogger) *CveStoreRepositoryImpl { + return &CveStoreRepositoryImpl{ + dbConnection: dbConnection, + logger: logger, + } +} + +func (impl CveStoreRepositoryImpl) GetConnection() (dbConnection *pg.DB) { + return impl.dbConnection +} + +func (impl CveStoreRepositoryImpl) Save(model *CveStore) error { + err := impl.dbConnection.Insert(model) + return err +} + +func (impl CveStoreRepositoryImpl) SaveInBatch(model []*CveStore, tx *pg.Tx) error { + err := tx.Insert(&model) + return err +} +func (impl CveStoreRepositoryImpl) FindAll() ([]*CveStore, error) { + var models []*CveStore + err := impl.dbConnection.Model(&models).Select() + return models, err +} + +func (impl CveStoreRepositoryImpl) FindByCveNames(names []string) ([]*CveStore, error) { + var models []*CveStore + err := impl.dbConnection.Model(&models).Where("name in (?)", pg.In(names)).Select() + return models, err +} + +func (impl CveStoreRepositoryImpl) FindByName(name string) (*CveStore, error) { + var model CveStore + err := impl.dbConnection.Model(&model). + Where("name = ?", name).Select() + return &model, err +} + +func (impl CveStoreRepositoryImpl) Update(team *CveStore) error { + err := impl.dbConnection.Update(team) + return err +} + +// UpdateInBatch updates the cve store model in bulk in db, returns the updated models +func (impl CveStoreRepositoryImpl) UpdateInBatch(models []*CveStore, tx *pg.Tx) ([]*CveStore, error) { + _, err := impl.dbConnection.Model(&models).Update() + if err != nil { + impl.logger.Errorw("error in UpdateInBatch CveStore", "err", err) + return nil, err + } + return models, nil +} diff --git a/image-scanner/pkg/sql/repository/DockerArtifactStoreRepository.go b/image-scanner/pkg/sql/repository/DockerArtifactStoreRepository.go new file mode 100644 index 000000000..4a1201014 --- /dev/null +++ b/image-scanner/pkg/sql/repository/DockerArtifactStoreRepository.go @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/devtron-labs/image-scanner/common" + "github.com/go-pg/pg" + "go.uber.org/zap" + "net/url" +) + +const ( + REGISTRYTYPE_ECR = "ecr" + REGISTRYTYPE_OTHER = "other" + REGISTRYTYPE_DOCKER_HUB = "docker-hub" + REGISTRYTYPE_GCR = "gcr" + REGISTRYTYPE_ARTIFACTREGISTRY = "artifact-registry" +) + +type DockerArtifactStore struct { + tableName struct{} `sql:"docker_artifact_store" json:",omitempty" pg:",discard_unknown_columns"` + Id string `sql:"id,pk" json:"id,,omitempty"` + PluginId string `sql:"plugin_id,notnull" json:"pluginId,omitempty"` + RemoteConnectionConfigId int `sql:"remote_connection_config_id" json:"remoteConnectionConfigId,omitempty"` + RegistryURL string `sql:"registry_url" json:"registryUrl,omitempty"` + RegistryType common.RegistryType `sql:"registry_type,notnull" json:"registryType,omitempty"` + AWSAccessKeyId string `sql:"aws_accesskey_id" json:"awsAccessKeyId,omitempty" ` + AWSSecretAccessKey string `sql:"aws_secret_accesskey" json:"awsSecretAccessKey,omitempty"` + AWSRegion string `sql:"aws_region" json:"awsRegion,omitempty"` + Username string `sql:"username" json:"username,omitempty"` + Password string `sql:"password" json:"password,omitempty"` + IsDefault bool `sql:"is_default,notnull" json:"isDefault"` + Connection string `sql:"connection" json:"connection,omitempty"` + Cert string `sql:"cert" json:"cert,omitempty"` + Active bool `sql:"active,notnull" json:"active"` + RemoteConnectionConfig *RemoteConnectionConfig + AuditLog +} + +func (store *DockerArtifactStore) GetRegistryLocation() (registryLocation string, err error) { + u, err := url.Parse(registryLocation) + if err != nil { + return "", err + } else { + return u.Host, nil + } +} + +type DockerArtifactStoreRepository interface { + FindActiveDefaultStore() (*DockerArtifactStore, error) + FindById(id string) (*DockerArtifactStore, error) +} + +type DockerArtifactStoreRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewDockerArtifactStoreRepositoryImpl(dbConnection *pg.DB, + logger *zap.SugaredLogger) *DockerArtifactStoreRepositoryImpl { + return &DockerArtifactStoreRepositoryImpl{ + dbConnection: dbConnection, + logger: logger, + } +} + +func (impl DockerArtifactStoreRepositoryImpl) FindActiveDefaultStore() (*DockerArtifactStore, error) { + store := &DockerArtifactStore{} + err := impl.dbConnection.Model(store). + Where("is_default = ?", true). + Where("active = ?", true).Select() + if err != nil { + impl.logger.Errorw("error in finding default docker registry details", "err", err) + } + return store, err +} + +func (impl DockerArtifactStoreRepositoryImpl) FindById(id string) (*DockerArtifactStore, error) { + var provider DockerArtifactStore + err := impl.dbConnection.Model(&provider). + Column("docker_artifact_store.*", "RemoteConnectionConfig"). + Where("docker_artifact_store.id = ?", id). + Where("docker_artifact_store.active = ?", true). + Select() + if err != nil { + impl.logger.Errorw("error in finding docker store details by id", "err", err, "id", id) + } + return &provider, err +} diff --git a/image-scanner/pkg/sql/repository/ImageScanDeployInfoRepository.go b/image-scanner/pkg/sql/repository/ImageScanDeployInfoRepository.go new file mode 100644 index 000000000..7858e5523 --- /dev/null +++ b/image-scanner/pkg/sql/repository/ImageScanDeployInfoRepository.go @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/devtron-labs/image-scanner/pkg/sql/bean" + "github.com/go-pg/pg" + "go.uber.org/zap" + "time" +) + +/* +* +this table contains scanned images registry for deployed object and apps, +images which are deployed on cluster by anyway and has scanned result +*/ +type ImageScanDeployInfo struct { + tableName struct{} `sql:"image_scan_deploy_info" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + ImageScanExecutionHistoryId []int `sql:"image_scan_execution_history_id,notnull" pg:",array"` + ScanObjectMetaId int `sql:"scan_object_meta_id,notnull"` + ObjectType string `sql:"object_type,notnull"` + EnvId *int `sql:"env_id"` + ClusterId *int `sql:"cluster_id"` + AuditLog +} + +func (r *ImageScanDeployInfo) CreateAuditLog() { + r.CreatedBy = bean.UserSystemId + r.CreatedOn = time.Now() + r.UpdatedBy = bean.UserSystemId + r.UpdatedOn = time.Now() +} +func (r *ImageScanDeployInfo) UpdateImageScanExecutionHistoryIdList(historyId int) { + r.ImageScanExecutionHistoryId = append(r.ImageScanExecutionHistoryId, historyId) + r.UpdatedOn = time.Now() +} + +const ( + ScanObjectType_APP string = "app" + ScanObjectType_CHART string = "chart" + ScanObjectType_POD string = "pod" + ScanObjectType_CHART_HISTORY string = "chart-history" + ScanObjectType_CI_Workflow string = "ci-workflow" + ScanObjectType_CD_Workflow string = "cd-workflow" +) + +type ImageScanDeployInfoRepository interface { + Save(model *ImageScanDeployInfo) error + FindAll() ([]*ImageScanDeployInfo, error) + FindOne(id int) (*ImageScanDeployInfo, error) + FindByIds(ids []int) ([]*ImageScanDeployInfo, error) + Update(model *ImageScanDeployInfo) error + FetchListingGroupByObject() ([]*ImageScanDeployInfo, error) + FetchByAppIdAndEnvId(appId int, envId int) (*ImageScanDeployInfo, error) + FindByObjectTypeAndId(scanObjectMetaId int, objectType string) (*ImageScanDeployInfo, error) + FindLatestChildParentInfoId(parentInfoId int) (*ImageScanDeployInfo, error) +} + +type ImageScanDeployInfoRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewImageScanDeployInfoRepositoryImpl(dbConnection *pg.DB, logger *zap.SugaredLogger) *ImageScanDeployInfoRepositoryImpl { + return &ImageScanDeployInfoRepositoryImpl{ + dbConnection: dbConnection, + logger: logger, + } +} + +func (impl ImageScanDeployInfoRepositoryImpl) Save(model *ImageScanDeployInfo) error { + err := impl.dbConnection.Insert(model) + return err +} + +func (impl ImageScanDeployInfoRepositoryImpl) FindAll() ([]*ImageScanDeployInfo, error) { + var models []*ImageScanDeployInfo + err := impl.dbConnection.Model(&models).Select() + return models, err +} + +func (impl ImageScanDeployInfoRepositoryImpl) FindOne(id int) (*ImageScanDeployInfo, error) { + var model ImageScanDeployInfo + err := impl.dbConnection.Model(&model). + Where("id = ?", id).Select() + return &model, err +} + +func (impl ImageScanDeployInfoRepositoryImpl) FindByIds(ids []int) ([]*ImageScanDeployInfo, error) { + var models []*ImageScanDeployInfo + err := impl.dbConnection.Model(&models).Where("id in (?)", pg.In(ids)).Select() + return models, err +} + +func (impl ImageScanDeployInfoRepositoryImpl) Update(team *ImageScanDeployInfo) error { + err := impl.dbConnection.Update(team) + return err +} + +func (impl ImageScanDeployInfoRepositoryImpl) FetchListingGroupByObject() ([]*ImageScanDeployInfo, error) { + var models []*ImageScanDeployInfo + /*err := impl.dbConnection.Model(&models). + Column("max(image_scan_deploy_info.id) as id", "image_scan_deploy_info.scan_object_meta_id"). + Group("image_scan_deploy_info.scan_object_meta_id"). + Order("image_scan_deploy_info.id desc").Select()*/ + query := "select scan_object_meta_id,object_type, max(id) as id from image_scan_deploy_info" + + " group by scan_object_meta_id,object_type order by id desc" + _, err := impl.dbConnection.Query(&models, query) + if err != nil { + impl.logger.Error("err", err) + return []*ImageScanDeployInfo{}, err + } + return models, err +} + +func (impl ImageScanDeployInfoRepositoryImpl) FetchByAppIdAndEnvId(appId int, envId int) (*ImageScanDeployInfo, error) { + var model ImageScanDeployInfo + err := impl.dbConnection.Model(&model). + Where("scan_object_meta_id = ?", appId). + Where("env_id = ?", envId).Where("object_type = ?", "app"). + Order("created_on desc").Limit(1). + Select() + return &model, err +} + +func (impl ImageScanDeployInfoRepositoryImpl) FindByObjectTypeAndId(scanObjectMetaId int, objectType string) (*ImageScanDeployInfo, error) { + var model ImageScanDeployInfo + err := impl.dbConnection.Model(&model). + Where("scan_object_meta_id = ?", scanObjectMetaId). + Where("object_type = ?", objectType). + Order("created_on desc").Limit(1). + Select() + if err == pg.ErrNoRows { + return nil, nil + } + return &model, err +} + +func (impl ImageScanDeployInfoRepositoryImpl) FindLatestChildParentInfoId(parentInfoId int) (*ImageScanDeployInfo, error) { + var model ImageScanDeployInfo + err := impl.dbConnection.Model(&model). + Where("parent_id = ?", parentInfoId). + Order("created_on desc").Limit(1). + Select() + if err == pg.ErrNoRows { + return nil, nil + } + return &model, err +} diff --git a/image-scanner/pkg/sql/repository/ImageScanHistoryRepository.go b/image-scanner/pkg/sql/repository/ImageScanHistoryRepository.go new file mode 100644 index 000000000..010c6dad7 --- /dev/null +++ b/image-scanner/pkg/sql/repository/ImageScanHistoryRepository.go @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/devtron-labs/image-scanner/common" + "github.com/go-pg/pg" + "go.uber.org/zap" + "time" +) + +type ImageScanExecutionHistory struct { + tableName struct{} `sql:"image_scan_execution_history" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + Image string `sql:"image,notnull"` + ImageHash string `sql:"image_hash,notnull"` // TODO Migrate to request metadata + ExecutionTime time.Time `sql:"execution_time"` + ExecutedBy int `sql:"executed_by,notnull"` + SourceMetadataJson string `sql:"source_metadata_json"` // to have relevant info to process a scan for a given source type and subtype + ExecutionHistoryDirectoryPath string `sql:"execution_history_directory_path"` // Deprecated + SourceType common.SourceType `sql:"source_type"` + SourceSubType common.SourceSubType `sql:"source_sub_type"` + ParentId int `sql:"parent_id"` + IsLatest bool `sql:"is_latest"` +} + +func (r *ImageScanExecutionHistory) IsSourceAndSubSourceTypeSame(sourceType common.SourceType, sourceSubType common.SourceSubType) bool { + return r.SourceType == sourceType && r.SourceSubType == sourceSubType +} + +func (r *ImageScanExecutionHistory) UpdateIsLatest(isLatest bool) *ImageScanExecutionHistory { + r.IsLatest = isLatest + return r +} + +func (r *ImageScanExecutionHistory) UpdateParentId(parentId int) { + r.ParentId = parentId +} + +//Refer image_scan_deploy_info table for source_type relation +// ci workflow will have scans for ci-code and ci artifact +// cd workflow will have scans for deployment manifest, manifest images +// helm chart will have scans for manifest images and manifest + +type ImageScanHistoryRepository interface { + GetConnection() (dbConnection *pg.DB) + Save(tx *pg.Tx, model *ImageScanExecutionHistory) error + FindAll() ([]*ImageScanExecutionHistory, error) + FindOne(id int) (*ImageScanExecutionHistory, error) + FindByImageDigest(image string) (*ImageScanExecutionHistory, error) + FindByImageDigests(digest []string) ([]*ImageScanExecutionHistory, error) + Update(model *ImageScanExecutionHistory) error + FindByImage(image string) (*ImageScanExecutionHistory, error) + FindByImageWithNoSource(image string) (*ImageScanExecutionHistory, error) + FindByImageWithSource(image string) (*ImageScanExecutionHistory, error) + FindLatestByParentScanHistory(parentScanHistoryId int) (*ImageScanExecutionHistory, error) +} + +type ImageScanHistoryRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewImageScanHistoryRepositoryImpl(dbConnection *pg.DB, logger *zap.SugaredLogger) *ImageScanHistoryRepositoryImpl { + return &ImageScanHistoryRepositoryImpl{ + dbConnection: dbConnection, + logger: logger, + } +} + +func (impl ImageScanHistoryRepositoryImpl) GetConnection() (dbConnection *pg.DB) { + return impl.dbConnection +} + +func (impl ImageScanHistoryRepositoryImpl) Save(tx *pg.Tx, model *ImageScanExecutionHistory) error { + err := tx.Insert(model) + return err +} + +func (impl ImageScanHistoryRepositoryImpl) FindAll() ([]*ImageScanExecutionHistory, error) { + var models []*ImageScanExecutionHistory + err := impl.dbConnection.Model(&models).Select() + return models, err +} + +func (impl ImageScanHistoryRepositoryImpl) FindOne(id int) (*ImageScanExecutionHistory, error) { + var model ImageScanExecutionHistory + err := impl.dbConnection.Model(&model). + Where("id = ?", id).Select() + return &model, err +} + +func (impl ImageScanHistoryRepositoryImpl) FindByImageDigest(image string) (*ImageScanExecutionHistory, error) { + var model ImageScanExecutionHistory + err := impl.dbConnection.Model(&model). + Where("image_hash = ?", image).Order("execution_time desc").Limit(1).Select() + return &model, err +} + +func (impl ImageScanHistoryRepositoryImpl) FindByImageDigests(digest []string) ([]*ImageScanExecutionHistory, error) { + var models []*ImageScanExecutionHistory + err := impl.dbConnection.Model(&models). + Where("image_hash in (?)", pg.In(digest)).Order("execution_time desc").Select() + return models, err +} + +func (impl ImageScanHistoryRepositoryImpl) Update(team *ImageScanExecutionHistory) error { + err := impl.dbConnection.Update(team) + return err +} + +func (impl ImageScanHistoryRepositoryImpl) FindByImage(image string) (*ImageScanExecutionHistory, error) { + var model ImageScanExecutionHistory + err := impl.dbConnection.Model(&model). + Where("image = ?", image).Order("execution_time desc").Limit(1).Select() + return &model, err +} + +func (impl ImageScanHistoryRepositoryImpl) FindByImageWithNoSource(image string) (*ImageScanExecutionHistory, error) { + var model ImageScanExecutionHistory + q := impl.dbConnection.Model(&model). + Where("image = ?", image).Where("source_type is null or source_type = 0").Where("source_type is null or source_type = 0") + + err := q.Order("execution_time desc"). + Limit(1).Select() + return &model, err +} + +func (impl ImageScanHistoryRepositoryImpl) FindByImageWithSource(image string) (*ImageScanExecutionHistory, error) { + var model ImageScanExecutionHistory + q := impl.dbConnection.Model(&model). + Where("image = ?", image).Where("source_type != 0 and source_type is not null") + + err := q.Order("execution_time desc"). + Limit(1).Select() + return &model, err +} + +func (impl ImageScanHistoryRepositoryImpl) FindLatestByParentScanHistory(parentScanHistoryId int) (*ImageScanExecutionHistory, error) { + var model ImageScanExecutionHistory + err := impl.dbConnection.Model(&model). + Where("parent_id = ?", parentScanHistoryId). + Where("is_latest = ?", true). + Select() + return &model, err +} diff --git a/image-scanner/pkg/sql/repository/ImageScanObjectMetaRepository.go b/image-scanner/pkg/sql/repository/ImageScanObjectMetaRepository.go new file mode 100644 index 000000000..8fc58d15e --- /dev/null +++ b/image-scanner/pkg/sql/repository/ImageScanObjectMetaRepository.go @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type ImageScanObjectMeta struct { + tableName struct{} `sql:"image_scan_object_meta" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + Name string `sql:"name,notnull"` + Image string `sql:"image,notnull"` + Active bool `sql:"active"` +} + +type ImageScanObjectMetaRepository interface { + Save(model *ImageScanObjectMeta) error + FindAll() ([]*ImageScanObjectMeta, error) + FindOne(id int) (*ImageScanObjectMeta, error) + FindByNameAndType(name string, types string) ([]*ImageScanObjectMeta, error) + Update(model *ImageScanObjectMeta) error +} + +type ImageScanObjectMetaRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewImageScanObjectMetaRepositoryImpl(dbConnection *pg.DB, logger *zap.SugaredLogger) *ImageScanObjectMetaRepositoryImpl { + return &ImageScanObjectMetaRepositoryImpl{ + dbConnection: dbConnection, + logger: logger, + } +} + +func (impl ImageScanObjectMetaRepositoryImpl) Save(model *ImageScanObjectMeta) error { + err := impl.dbConnection.Insert(model) + return err +} + +func (impl ImageScanObjectMetaRepositoryImpl) FindAll() ([]*ImageScanObjectMeta, error) { + var models []*ImageScanObjectMeta + err := impl.dbConnection.Model(&models).Where("active=?", true).Select() + return models, err +} + +func (impl ImageScanObjectMetaRepositoryImpl) FindOne(id int) (*ImageScanObjectMeta, error) { + var model *ImageScanObjectMeta + err := impl.dbConnection.Model(&model). + Where("id = ?", id).Select() + return model, err +} + +func (impl ImageScanObjectMetaRepositoryImpl) FindByNameAndType(name string, types string) ([]*ImageScanObjectMeta, error) { + var models []*ImageScanObjectMeta + err := impl.dbConnection.Model(&models). + Where("cve_name = ?", name).Select() + return models, err +} + +func (impl ImageScanObjectMetaRepositoryImpl) Update(team *ImageScanObjectMeta) error { + err := impl.dbConnection.Update(team) + return err +} diff --git a/image-scanner/pkg/sql/repository/ImageScanResultRepository.go b/image-scanner/pkg/sql/repository/ImageScanResultRepository.go new file mode 100644 index 000000000..13593cf0d --- /dev/null +++ b/image-scanner/pkg/sql/repository/ImageScanResultRepository.go @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type ImageScanExecutionResult struct { + tableName struct{} `sql:"image_scan_execution_result" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + CveStoreName string `sql:"cve_store_name,notnull"` + ImageScanExecutionHistoryId int `sql:"image_scan_execution_history_id"` //TODO: remove this + ScanToolId int `sql:"scan_tool_id"` + Package string `sql:"package"` + Version string `sql:"version"` + FixedVersion string `sql:"fixed_version"` + Target string `sql:"target"` + Type string `sql:"type"` + Class string `sql:"class"` + CveStore CveStore + ImageScanExecutionHistory ImageScanExecutionHistory +} + +type ImageScanResultRepository interface { + Save(model *ImageScanExecutionResult) error + SaveInBatch(models []*ImageScanExecutionResult, tx *pg.Tx) error + FindAll() ([]*ImageScanExecutionResult, error) + FindOne(id int) (*ImageScanExecutionResult, error) + FindByCveName(name string) ([]*ImageScanExecutionResult, error) + Update(model *ImageScanExecutionResult) error + FetchByScanExecutionId(id int) ([]*ImageScanExecutionResult, error) + FetchByScanExecutionIds(ids []int) ([]*ImageScanExecutionResult, error) +} + +type ImageScanResultRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewImageScanResultRepositoryImpl(dbConnection *pg.DB, logger *zap.SugaredLogger) *ImageScanResultRepositoryImpl { + return &ImageScanResultRepositoryImpl{ + dbConnection: dbConnection, + logger: logger, + } +} + +func (impl ImageScanResultRepositoryImpl) Save(model *ImageScanExecutionResult) error { + err := impl.dbConnection.Insert(model) + return err +} + +func (impl ImageScanResultRepositoryImpl) SaveInBatch(models []*ImageScanExecutionResult, tx *pg.Tx) error { + err := tx.Insert(&models) + return err +} + +func (impl ImageScanResultRepositoryImpl) FindAll() ([]*ImageScanExecutionResult, error) { + var models []*ImageScanExecutionResult + err := impl.dbConnection.Model(&models).Select() + return models, err +} + +func (impl ImageScanResultRepositoryImpl) FindOne(id int) (*ImageScanExecutionResult, error) { + var model *ImageScanExecutionResult + err := impl.dbConnection.Model(&model). + Where("id = ?", id).Select() + return model, err +} + +func (impl ImageScanResultRepositoryImpl) FindByCveName(name string) ([]*ImageScanExecutionResult, error) { + var model []*ImageScanExecutionResult + err := impl.dbConnection.Model(&model). + Where("cve_store_name = ?", name).Select() + return model, err +} + +func (impl ImageScanResultRepositoryImpl) Update(team *ImageScanExecutionResult) error { + err := impl.dbConnection.Update(team) + return err +} + +func (impl ImageScanResultRepositoryImpl) FetchByScanExecutionId(scanExecutionId int) ([]*ImageScanExecutionResult, error) { + var models []*ImageScanExecutionResult + err := impl.dbConnection.Model(&models).Column("image_scan_execution_result.*", "CveStore"). + Where("image_scan_execution_result.image_scan_execution_history_id = ?", scanExecutionId). + Select() + return models, err +} + +func (impl ImageScanResultRepositoryImpl) FetchByScanExecutionIds(ids []int) ([]*ImageScanExecutionResult, error) { + var models []*ImageScanExecutionResult + err := impl.dbConnection.Model(&models).Column("image_scan_execution_result.*", "ImageScanExecutionHistory", "CveStore"). + Where("image_scan_execution_result.image_scan_execution_history_id in(?)", pg.In(ids)). + Select() + return models, err +} diff --git a/image-scanner/pkg/sql/repository/RegistryIndexMappingRepository.go b/image-scanner/pkg/sql/repository/RegistryIndexMappingRepository.go new file mode 100644 index 000000000..34933e551 --- /dev/null +++ b/image-scanner/pkg/sql/repository/RegistryIndexMappingRepository.go @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/devtron-labs/image-scanner/common" + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type RegistryIndexMapping struct { + tableName struct{} `sql:"registry_index_mapping" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + Registry common.RegistryType `sql:"registry_type"` + Index int `sql:"starting_index"` + ScanToolId int `sql:"scan_tool_id"` +} + +type RegistryIndexMappingRepositoryImpl struct { + DbConnection *pg.DB + Logger *zap.SugaredLogger +} + +func NewRegistryIndexMappingRepositoryImpl(dbConnection *pg.DB, + logger *zap.SugaredLogger) *RegistryIndexMappingRepositoryImpl { + return &RegistryIndexMappingRepositoryImpl{ + DbConnection: dbConnection, + Logger: logger, + } +} + +type RegistryIndexMappingRepository interface { + GetStartingIndexForARegistryAndATool(scanToolid int, registry common.RegistryType) (*RegistryIndexMapping, error) +} + +func (repo *RegistryIndexMappingRepositoryImpl) GetStartingIndexForARegistryAndATool(scanToolid int, registry common.RegistryType) (*RegistryIndexMapping, error) { + var model RegistryIndexMapping + err := repo.DbConnection.Model(&model).Where("scan_tool_id = ?", scanToolid).Where("registry_type = ?", registry).Select() + if err != nil { + return &model, err + } + return &model, nil +} diff --git a/image-scanner/pkg/sql/repository/ResourceScanExecutionResultRepository.go b/image-scanner/pkg/sql/repository/ResourceScanExecutionResultRepository.go new file mode 100644 index 000000000..c79adadbc --- /dev/null +++ b/image-scanner/pkg/sql/repository/ResourceScanExecutionResultRepository.go @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type ResourceScanResult struct { + tableName struct{} `sql:"resource_scan_execution_result" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + ImageScanExecutionHistoryId int `sql:"image_scan_execution_history_id"` + ScanDataJson string `sql:"scan_data_json"` + Format ResourceScanFormat `sql:"format"` + Types []int `sql:"types" pg:",array"` + ScanToolId int `sql:"scan_tool_id"` +} + +type ResourceScanFormat int + +const ( + CycloneDxSbom ResourceScanFormat = 1 //SBOM + TrivyJson = 2 + Json = 3 +) + +type ResourceScanType int + +const ( + Vulnerabilities ResourceScanType = 1 + License ResourceScanType = 2 + Config ResourceScanType = 3 + Secrets ResourceScanType = 4 +) + +func (t ResourceScanType) ToInt() int { + return int(t) +} + +type ResourceScanResultRepository interface { + SaveInBatch(models []*ResourceScanResult) error + FetchByScanHistoryIdAndFormatType(scanHistoryId int, format int) ([]*ResourceScanResult, error) +} + +type ResourceScanResultRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewResourceScanResultRepositoryImpl(dbConnection *pg.DB, logger *zap.SugaredLogger) *ResourceScanResultRepositoryImpl { + return &ResourceScanResultRepositoryImpl{ + dbConnection: dbConnection, + logger: logger, + } +} + +func (impl ResourceScanResultRepositoryImpl) SaveInBatch(models []*ResourceScanResult) error { + return impl.dbConnection.Insert(&models) +} + +func (impl ResourceScanResultRepositoryImpl) FetchByScanHistoryIdAndFormatType(scanHistoryId int, format int) ([]*ResourceScanResult, error) { + var model []*ResourceScanResult + err := impl.dbConnection.Model(&model). + Where("image_scan_execution_history_id = ?", scanHistoryId). + Where("format = ?", format). + Select() + if err != nil { + return model, err + } + + return model, nil +} diff --git a/image-scanner/pkg/sql/repository/ScanStepConditionMappingRepository.go b/image-scanner/pkg/sql/repository/ScanStepConditionMappingRepository.go new file mode 100644 index 000000000..bfc5c03a8 --- /dev/null +++ b/image-scanner/pkg/sql/repository/ScanStepConditionMappingRepository.go @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type ScanStepConditionMapping struct { + tableName struct{} `sql:"scan_step_condition_mapping" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + ScanStepConditionId int `sql:"scan_step_condition_id"` + ScanStepConditionMappingId int `sql:"scan_tool_step_id"` + ScanToolMetadata + ScanStepCondition + AuditLog +} + +type ScanStepConditionMappingRepository interface { + Save(model *ScanStepConditionMapping) (*ScanStepConditionMapping, error) + Update(model *ScanStepConditionMapping) (*ScanStepConditionMapping, error) + SaveBulk(model []*ScanStepConditionMapping) ([]*ScanStepConditionMapping, error) + UpdateBulk(model []*ScanStepConditionMapping) ([]*ScanStepConditionMapping, error) +} + +type ScanStepConditionMappingRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewScanStepConditionMappingRepositoryImpl(dbConnection *pg.DB, + logger *zap.SugaredLogger) *ScanStepConditionMappingRepositoryImpl { + return &ScanStepConditionMappingRepositoryImpl{ + dbConnection: dbConnection, + logger: logger, + } +} + +func (repo *ScanStepConditionMappingRepositoryImpl) Save(model *ScanStepConditionMapping) (*ScanStepConditionMapping, error) { + err := repo.dbConnection.Insert(model) + if err != nil { + repo.logger.Errorw("error in saving scan step condition mapping", "err", err) + return nil, err + } + return model, nil +} + +func (repo *ScanStepConditionMappingRepositoryImpl) Update(model *ScanStepConditionMapping) (*ScanStepConditionMapping, error) { + err := repo.dbConnection.Update(model) + if err != nil { + repo.logger.Errorw("error in updating scan step condition mapping", "err", err) + return nil, err + } + return model, nil +} + +func (repo *ScanStepConditionMappingRepositoryImpl) SaveBulk(model []*ScanStepConditionMapping) ([]*ScanStepConditionMapping, error) { + err := repo.dbConnection.Insert(&model) + if err != nil { + repo.logger.Errorw("error in saving scan step condition mapping in bulk", "err", err) + return nil, err + } + return model, nil +} + +func (repo *ScanStepConditionMappingRepositoryImpl) UpdateBulk(model []*ScanStepConditionMapping) ([]*ScanStepConditionMapping, error) { + _, err := repo.dbConnection.Model(&model).Update() + if err != nil { + repo.logger.Errorw("error in updating scan step condition mapping in bulk", "err", err) + return nil, err + } + return model, nil +} diff --git a/image-scanner/pkg/sql/repository/ScanStepConditionRepository.go b/image-scanner/pkg/sql/repository/ScanStepConditionRepository.go new file mode 100644 index 000000000..d451cf0a5 --- /dev/null +++ b/image-scanner/pkg/sql/repository/ScanStepConditionRepository.go @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/devtron-labs/image-scanner/pkg/sql/bean" + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type ScanStepCondition struct { + tableName struct{} `sql:"scan_step_condition" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + ConditionVariableFormat bean.VariableFormat `sql:"condition_variable_format"` + ConditionalOperator string `sql:"conditional_operator"` + ConditionalValue string `sql:"conditional_value"` + ConditionOn string `sql:"condition_on"` //json path of variable on which condition is to be applied + Deleted bool `sql:"deleted,notnull"` + AuditLog +} + +type ScanStepConditionRepository interface { + Save(model *ScanStepCondition) (*ScanStepCondition, error) + Update(model *ScanStepCondition) (*ScanStepCondition, error) + SaveBulk(model []*ScanStepCondition) ([]*ScanStepCondition, error) + UpdateBulk(model []*ScanStepCondition) ([]*ScanStepCondition, error) + FindAllByToolStepId(toolStepId int) ([]*ScanStepCondition, error) + MarkDeletedById(id int) error + MarkAllConditionsDeletedByToolStepId(toolStepId int) error +} + +type ScanStepConditionRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewScanStepConditionRepositoryImpl(dbConnection *pg.DB, + logger *zap.SugaredLogger) *ScanStepConditionRepositoryImpl { + return &ScanStepConditionRepositoryImpl{ + dbConnection: dbConnection, + logger: logger, + } +} + +func (repo *ScanStepConditionRepositoryImpl) Save(model *ScanStepCondition) (*ScanStepCondition, error) { + err := repo.dbConnection.Insert(model) + if err != nil { + repo.logger.Errorw("error in saving scan step condition ", "err", err) + return nil, err + } + return model, nil +} + +func (repo *ScanStepConditionRepositoryImpl) Update(model *ScanStepCondition) (*ScanStepCondition, error) { + err := repo.dbConnection.Update(model) + if err != nil { + repo.logger.Errorw("error in updating scan step condition ", "err", err) + return nil, err + } + return model, nil +} + +func (repo *ScanStepConditionRepositoryImpl) SaveBulk(model []*ScanStepCondition) ([]*ScanStepCondition, error) { + err := repo.dbConnection.Insert(&model) + if err != nil { + repo.logger.Errorw("error in saving scan step condition in bulk", "err", err) + return nil, err + } + return model, nil +} + +func (repo *ScanStepConditionRepositoryImpl) UpdateBulk(model []*ScanStepCondition) ([]*ScanStepCondition, error) { + _, err := repo.dbConnection.Model(&model).Update() + if err != nil { + repo.logger.Errorw("error in updating scan step condition in bulk", "err", err) + return nil, err + } + return model, nil +} + +func (repo *ScanStepConditionRepositoryImpl) FindAllByToolStepId(toolStepId int) ([]*ScanStepCondition, error) { + var model []*ScanStepCondition + err := repo.dbConnection.Model(&model). + Join("LEFT JOIN scan_step_condition_mapping sscp ON scan_step_condition.id=sscp.scan_step_condition_id"). + Where("sscp.scan_tool_step_id = ?", toolStepId). + Where("scan_step_condition.deleted = ?", false).Select() + if err != nil { + repo.logger.Errorw("error in getting scan step conditions by step id", "err", err, "toolStepId", toolStepId) + return nil, err + } + return model, nil +} + +func (repo *ScanStepConditionRepositoryImpl) MarkDeletedById(id int) error { + model := &ScanStepCondition{} + _, err := repo.dbConnection.Model(model).Set("deleted = ?", true). + Where("id = ?", id).Update() + if err != nil { + repo.logger.Errorw("error in marking condition deleted by id", "err", err, "id", id) + return err + } + return nil +} + +func (repo *ScanStepConditionRepositoryImpl) MarkAllConditionsDeletedByToolStepId(toolStepId int) error { + model := &ScanStepCondition{} + _, err := repo.dbConnection.Model(model).Set("deleted = ?", true). + Join("LEFT JOIN scan_step_condition_mapping sscp ON scan_step_condition.id=sscp.scan_step_condition_id"). + Where("sscp.scan_tool_step_id = ?", toolStepId).Update() + if err != nil { + repo.logger.Errorw("error in marking conditions deleted by tool step id", "err", err, "toolStepid", toolStepId) + return err + } + return nil +} diff --git a/image-scanner/pkg/sql/repository/ScanToolExecutionHistoryMappingRepository.go b/image-scanner/pkg/sql/repository/ScanToolExecutionHistoryMappingRepository.go new file mode 100644 index 000000000..114685782 --- /dev/null +++ b/image-scanner/pkg/sql/repository/ScanToolExecutionHistoryMappingRepository.go @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/devtron-labs/image-scanner/pkg/sql/bean" + "github.com/go-pg/pg" + "go.uber.org/zap" + "time" +) + +type ScanToolExecutionHistoryMapping struct { + tableName struct{} `sql:"scan_tool_execution_history_mapping" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + ImageScanExecutionHistoryId int `sql:"image_scan_execution_history_id"` + ScanToolId int `sql:"scan_tool_id"` + ExecutionStartTime time.Time `sql:"execution_start_time,notnull"` + ExecutionFinishTime time.Time `sql:"execution_finish_time,notnull"` + State bean.ScanExecutionProcessState `sql:"state"` + ErrorMessage string `sql:"error_message"` + TryCount int `sql:"try_count"` + AuditLog +} + +type ScanToolExecutionHistoryMappingRepository interface { + Save(tx *pg.Tx, model *ScanToolExecutionHistoryMapping) error + SaveInBatch(models []*ScanToolExecutionHistoryMapping) error + UpdateStateByToolAndExecutionHistoryId(executionHistoryId, toolId int, state bean.ScanExecutionProcessState, executionFinishTime time.Time, errorMessage string) error + MarkAllRunningStateAsFailedHavingTryCountReachedLimit(tryCount int) error + GetAllScanHistoriesByState(state bean.ScanExecutionProcessState) ([]*ScanToolExecutionHistoryMapping, error) + GetAllScanHistoriesByExecutionHistoryIdAndStates(executionHistoryId int, states []bean.ScanExecutionProcessState) ([]*ScanToolExecutionHistoryMapping, error) +} + +type ScanToolExecutionHistoryMappingRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewScanToolExecutionHistoryMappingRepositoryImpl(dbConnection *pg.DB, + logger *zap.SugaredLogger) *ScanToolExecutionHistoryMappingRepositoryImpl { + return &ScanToolExecutionHistoryMappingRepositoryImpl{ + dbConnection: dbConnection, + logger: logger, + } +} + +func (repo *ScanToolExecutionHistoryMappingRepositoryImpl) Save(tx *pg.Tx, model *ScanToolExecutionHistoryMapping) error { + err := tx.Insert(model) + if err != nil { + repo.logger.Errorw("error in ScanToolExecutionHistoryMappingRepository, Save", "model", model, "err", err) + return err + } + return nil +} + +func (repo *ScanToolExecutionHistoryMappingRepositoryImpl) SaveInBatch(models []*ScanToolExecutionHistoryMapping) error { + err := repo.dbConnection.Insert(&models) + if err != nil { + repo.logger.Errorw("error in ScanToolExecutionHistoryMappingRepository, SaveInBatch", "err", err, "models", models) + return err + } + return nil +} + +func (repo *ScanToolExecutionHistoryMappingRepositoryImpl) UpdateStateByToolAndExecutionHistoryId(executionHistoryId, toolId int, + state bean.ScanExecutionProcessState, executionFinishTime time.Time, errorMessage string) error { + model := &ScanToolExecutionHistoryMapping{} + _, err := repo.dbConnection.Model(model). + Set("state = ?", state). + Set("error_message = ?", errorMessage). + Set("execution_finish_time = ?", executionFinishTime). + Where("image_scan_execution_history_id = ?", executionHistoryId). + Where("scan_tool_id = ?", toolId).Update() + if err != nil { + repo.logger.Errorw("error in ScanToolExecutionHistoryMappingRepository, SaveInBatch", "err", err, "model", model) + return err + } + return nil +} + +func (repo *ScanToolExecutionHistoryMappingRepositoryImpl) MarkAllRunningStateAsFailedHavingTryCountReachedLimit(tryCount int) error { + var models []*ScanToolExecutionHistoryMapping + _, err := repo.dbConnection.Model(&models). + Set("state = ?", bean.ScanExecutionProcessStateFailed). + Where("state = ?", bean.ScanExecutionProcessStateRunning). + Where("try_count > ?", tryCount).Update() + if err != nil { + repo.logger.Errorw("error in ScanToolExecutionHistoryMappingRepository, MarkAllRunningStateAsFailedHavingTryCountReachedLimit", "err", err) + return err + } + return nil +} + +func (repo *ScanToolExecutionHistoryMappingRepositoryImpl) GetAllScanHistoriesByState(state bean.ScanExecutionProcessState) ([]*ScanToolExecutionHistoryMapping, error) { + var models []*ScanToolExecutionHistoryMapping + err := repo.dbConnection.Model(&models).Column("scan_tool_execution_history_mapping.*"). + Where("state = ?", state).Select() + if err != nil { + repo.logger.Errorw("error in ScanToolExecutionHistoryMappingRepository, GetAllScanHistoriesByState", "err", err) + return nil, err + } + return models, nil +} + +func (repo *ScanToolExecutionHistoryMappingRepositoryImpl) GetAllScanHistoriesByExecutionHistoryIdAndStates(executionHistoryId int, states []bean.ScanExecutionProcessState) ([]*ScanToolExecutionHistoryMapping, error) { + var models []*ScanToolExecutionHistoryMapping + err := repo.dbConnection.Model(&models).Column("scan_tool_execution_history_mapping.*"). + Where("image_scan_execution_history_id = ?", executionHistoryId). + Where("state in (?)", pg.In(states)).Select() + if err != nil { + repo.logger.Errorw("error in ScanToolExecutionHistoryMappingRepository, GetAllScanHistoriesByState", "err", err) + return nil, err + } + return models, nil +} diff --git a/image-scanner/pkg/sql/repository/ScanToolMetadataRepository.go b/image-scanner/pkg/sql/repository/ScanToolMetadataRepository.go new file mode 100644 index 000000000..9e724fbc4 --- /dev/null +++ b/image-scanner/pkg/sql/repository/ScanToolMetadataRepository.go @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type ScanTargetType string + +const ( + ImageScanTargetType ScanTargetType = "IMAGE" + CodeScanTargetType ScanTargetType = "CODE" +) + +type ScanToolMetadata struct { + tableName struct{} `sql:"scan_tool_metadata" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + Name string `sql:"name"` + Version string `sql:"version"` + ServerBaseUrl string `sql:"server_base_url"` + ResultDescriptorTemplate string `sql:"result_descriptor_template"` + ScanTarget ScanTargetType `sql:"scan_target"` + Active bool `sql:"active,notnull"` + Deleted bool `sql:"deleted,notnull"` + ToolMetaData string `sql:"tool_metadata"` + AuditLog +} + +type ScanToolMetadataRepository interface { + FindActiveToolByScanTarget(scanTarget ScanTargetType) (*ScanToolMetadata, error) + FindByNameAndVersion(name, version string) (*ScanToolMetadata, error) + FindActiveById(id int) (*ScanToolMetadata, error) + Save(model *ScanToolMetadata) (*ScanToolMetadata, error) + Update(model *ScanToolMetadata) (*ScanToolMetadata, error) + MarkToolDeletedById(id int) error + FindAllActiveTools() ([]*ScanToolMetadata, error) +} + +type ScanToolMetadataRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewScanToolMetadataRepositoryImpl(dbConnection *pg.DB, + logger *zap.SugaredLogger) *ScanToolMetadataRepositoryImpl { + return &ScanToolMetadataRepositoryImpl{ + dbConnection: dbConnection, + logger: logger, + } +} + +func (repo *ScanToolMetadataRepositoryImpl) FindActiveToolByScanTarget(scanTargetType ScanTargetType) (*ScanToolMetadata, error) { + var model ScanToolMetadata + err := repo.dbConnection.Model(&model).Where("active = ?", true). + Where("scan_target = ?", scanTargetType). + Where("deleted = ?", false).Limit(1).Select() + if err != nil { + repo.logger.Errorw("error in getting active tool for scan target", "err", err, "scanTargetType", scanTargetType) + return nil, err + } + return &model, nil +} + +func (repo *ScanToolMetadataRepositoryImpl) FindByNameAndVersion(name, version string) (*ScanToolMetadata, error) { + model := &ScanToolMetadata{} + err := repo.dbConnection.Model(model).Where("active = ?", true). + Where("name = ?", name).Where("version = ?", version). + Where("deleted = ?", false).Select() + if err != nil { + repo.logger.Errorw("error in getting tool by name and version", "err", err, "name", name, "version", version) + return nil, err + } + return model, nil +} + +func (repo *ScanToolMetadataRepositoryImpl) FindActiveById(id int) (*ScanToolMetadata, error) { + model := &ScanToolMetadata{} + err := repo.dbConnection.Model(model).Where("id = ?", id). + Where("active = ?", true). + Where("deleted = ?", false).Select() + if err != nil { + repo.logger.Errorw("error in getting active by id", "err", err, "id", id) + return nil, err + } + return model, nil +} + +func (repo *ScanToolMetadataRepositoryImpl) Save(model *ScanToolMetadata) (*ScanToolMetadata, error) { + err := repo.dbConnection.Insert(model) + if err != nil { + repo.logger.Errorw("error in saving scan tool metadata", "err", err, "model", model) + return nil, err + } + return model, nil +} + +func (repo *ScanToolMetadataRepositoryImpl) Update(model *ScanToolMetadata) (*ScanToolMetadata, error) { + err := repo.dbConnection.Update(model) + if err != nil { + repo.logger.Errorw("error in updating scan tool metadata", "err", err, "model", model) + return nil, err + } + return model, nil +} + +func (repo *ScanToolMetadataRepositoryImpl) MarkToolDeletedById(id int) error { + model := &ScanToolMetadata{} + _, err := repo.dbConnection.Model(model).Set("deleted = ?", true). + Where("id = ?", id).Update() + if err != nil { + repo.logger.Errorw("error in marking tool entry deleted by id", "err", err, "id", id) + return err + } + return nil +} +func (repo *ScanToolMetadataRepositoryImpl) FindAllActiveTools() ([]*ScanToolMetadata, error) { + var models []*ScanToolMetadata + err := repo.dbConnection.Model(&models).Where("active = ?", true). + Where("deleted = ?", false).Select() + if err != nil { + repo.logger.Errorw("error in getting active tool for scan target", "err", err) + return nil, err + } + return models, nil + +} diff --git a/image-scanner/pkg/sql/repository/ScanToolStepRepository.go b/image-scanner/pkg/sql/repository/ScanToolStepRepository.go new file mode 100644 index 000000000..e91d0749c --- /dev/null +++ b/image-scanner/pkg/sql/repository/ScanToolStepRepository.go @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "encoding/json" + cli_util "github.com/devtron-labs/image-scanner/internals/step-lib/util/cli-util" + "github.com/devtron-labs/image-scanner/pkg/sql/bean" + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type ScanToolStep struct { + tableName struct{} `sql:"scan_tool_step" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + ScanToolId int `sql:"scan_tool_id"` + Index int `sql:"index"` + StepExecutionType bean.ScanExecutionType `sql:"step_execution_type"` + StepExecutionSync bool `sql:"step_execution_sync,notnull"` //sync if true, else async + RetryCount int `sql:"retry_count"` //only applicable if step fails + ExecuteStepOnFail int `sql:"execute_step_on_fail"` //fail means that at least one condition is not matched (not applicable for async process) + ExecuteStepOnPass int `sql:"execute_step_on_pass"` //pass means that all conditions are matched + RenderInputDataFromStep int `sql:"render_input_data_from_step"` //use this steps output to render input data, -1 if not needed + HttpInputPayload json.RawMessage `sql:"http_input_payload"` + HttpMethodType string `sql:"http_method_type"` + HttpReqHeaders json.RawMessage `sql:"http_req_headers"` + HttpQueryParams json.RawMessage `sql:"http_query_params"` + CliCommand string `sql:"cli_command"` //consists of sub command and flags along with applicable values + CliOutputType cli_util.CliOutputType `sql:"cli_output_type"` + Deleted bool `sql:"deleted,notnull"` + AuditLog +} + +type ScanToolStepRepository interface { + Save(model *ScanToolStep) (*ScanToolStep, error) + Update(model *ScanToolStep) (*ScanToolStep, error) + SaveInBatch(model []*ScanToolStep) ([]*ScanToolStep, error) + UpdateInBatch(model []*ScanToolStep) ([]*ScanToolStep, error) + FindAllByScanToolId(scanToolId int) ([]*ScanToolStep, error) + MarkDeletedById(id int) error + MarkAllStepsDeletedByToolId(scanToolId int) error +} + +type ScanToolStepRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewScanToolStepRepositoryImpl(dbConnection *pg.DB, + logger *zap.SugaredLogger) *ScanToolStepRepositoryImpl { + return &ScanToolStepRepositoryImpl{ + dbConnection: dbConnection, + logger: logger, + } +} + +func (repo *ScanToolStepRepositoryImpl) Save(model *ScanToolStep) (*ScanToolStep, error) { + err := repo.dbConnection.Insert(model) + if err != nil { + repo.logger.Errorw("error in saving scan tool step", "err", err) + return nil, err + } + return model, nil +} + +func (repo *ScanToolStepRepositoryImpl) Update(model *ScanToolStep) (*ScanToolStep, error) { + err := repo.dbConnection.Update(model) + if err != nil { + repo.logger.Errorw("error in updating scan tool step", "err", err) + return nil, err + } + return model, nil +} + +func (repo *ScanToolStepRepositoryImpl) SaveInBatch(model []*ScanToolStep) ([]*ScanToolStep, error) { + err := repo.dbConnection.Insert(&model) + if err != nil { + repo.logger.Errorw("error in saving scan tool step in bulk", "err", err) + return nil, err + } + return model, nil +} + +func (repo *ScanToolStepRepositoryImpl) UpdateInBatch(model []*ScanToolStep) ([]*ScanToolStep, error) { + _, err := repo.dbConnection.Model(&model).Update() + if err != nil { + repo.logger.Errorw("error in updating scan tool step in bulk", "err", err) + return nil, err + } + return model, nil +} + +func (repo *ScanToolStepRepositoryImpl) FindAllByScanToolId(scanToolId int) ([]*ScanToolStep, error) { + var model []*ScanToolStep + err := repo.dbConnection.Model(&model).Where("scan_tool_id = ?", scanToolId). + Where("deleted = ?", false).Order("index ASC").Select() //ordering by index to get in order of execution + if err != nil { + repo.logger.Errorw("error in getting scan tool steps by tool id", "err", err, "scanToolId", scanToolId) + return nil, err + } + return model, nil +} + +func (repo *ScanToolStepRepositoryImpl) MarkDeletedById(id int) error { + model := &ScanToolStep{} + _, err := repo.dbConnection.Model(model).Set("deleted = ?", true). + Where("id = ?", id).Update() + if err != nil { + repo.logger.Errorw("error in marking step entry deleted by id", "err", err, "id", id) + return err + } + return nil +} + +func (repo *ScanToolStepRepositoryImpl) MarkAllStepsDeletedByToolId(scanToolId int) error { + model := &ScanToolStep{} + _, err := repo.dbConnection.Model(model).Set("deleted = ?", true). + Where("scan_tool_id = ?", scanToolId).Update() + if err != nil { + repo.logger.Errorw("error in marking steps entry deleted by tool id", "err", err, "scanToolId", scanToolId) + return err + } + return nil +} diff --git a/image-scanner/pkg/sql/repository/ServerConnectionRepository.go b/image-scanner/pkg/sql/repository/ServerConnectionRepository.go new file mode 100644 index 000000000..2f07dd407 --- /dev/null +++ b/image-scanner/pkg/sql/repository/ServerConnectionRepository.go @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package repository + +import ( + "github.com/devtron-labs/common-lib/utils/remoteConnection/bean" + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type RemoteConnectionRepository interface { + GetById(id int) (*RemoteConnectionConfig, error) +} + +type RemoteConnectionRepositoryImpl struct { + logger *zap.SugaredLogger + dbConnection *pg.DB +} + +func NewRemoteConnectionRepositoryImpl(dbConnection *pg.DB, logger *zap.SugaredLogger) *RemoteConnectionRepositoryImpl { + return &RemoteConnectionRepositoryImpl{ + logger: logger, + dbConnection: dbConnection, + } +} + +type RemoteConnectionConfig struct { + tableName struct{} `sql:"remote_connection_config" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + ConnectionMethod bean.RemoteConnectionMethod `sql:"connection_method"` + ProxyUrl string `sql:"proxy_url"` + SSHServerAddress string `sql:"ssh_server_address"` + SSHUsername string `sql:"ssh_username"` + SSHPassword string `sql:"ssh_password"` + SSHAuthKey string `sql:"ssh_auth_key"` + Deleted bool `sql:"deleted,notnull"` + AuditLog +} + +func (repo *RemoteConnectionRepositoryImpl) GetById(id int) (*RemoteConnectionConfig, error) { + model := &RemoteConnectionConfig{} + err := repo.dbConnection.Model(model). + Where("id = ?", id). + Where("deleted = ?", false). + Select() + if err != nil && err != pg.ErrNoRows { + repo.logger.Errorw("error in getting server connection config", "err", err, "id", id) + return nil, err + } + return model, nil +} diff --git a/image-scanner/pkg/sql/repository/UserRepository.go b/image-scanner/pkg/sql/repository/UserRepository.go new file mode 100644 index 000000000..da9379e17 --- /dev/null +++ b/image-scanner/pkg/sql/repository/UserRepository.go @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +@author: vikram@github.com/devtron-labs +@description: user crud +*/ +package repository + +import ( + "fmt" + "github.com/devtron-labs/image-scanner/pkg/sql/bean" + "github.com/go-pg/pg" + "go.uber.org/zap" + "time" +) + +type UserRepository interface { + CreateUser(userModel *UserModel, tx *pg.Tx) (*UserModel, error) + UpdateUser(userModel *UserModel, tx *pg.Tx) (*UserModel, error) + GetById(id int32) (*UserModel, error) + GetAll() ([]UserModel, error) + GetUsersByFilter(size int, from int) ([]UserModel, error) + FetchUserByEmail(email string) (bean.UserInfo, error) + FetchUserByEmailV2(email string) (*UserModel, error) + FetchUserDetailByEmailV2(email string) (*UserModel, error) + GetByIds(ids []int32) ([]UserModel, error) + DeleteUser(userModel *UserModel, tx *pg.Tx) (bool, error) + + GetConnection() (dbConnection *pg.DB) + GetByEmailId(email string) ([]UserModel, error) +} + +type UserRepositoryImpl struct { + dbConnection *pg.DB + Logger *zap.SugaredLogger +} + +func NewUserRepositoryImpl(dbConnection *pg.DB) *UserRepositoryImpl { + return &UserRepositoryImpl{dbConnection: dbConnection} +} + +type UserModel struct { + TableName struct{} `sql:"users"` + Id int32 `sql:"id,pk"` + EmailId string `sql:"email_id,notnull"` + AccessToken string `sql:"access_token"` + Active bool `sql:"active,notnull"` + AuditLog +} +type UserRoleModel struct { + TableName struct{} `sql:"user_roles"` + Id int `sql:"id,pk"` + UserId int32 `sql:"user_id,notnull"` + RoleId int `sql:"role_id,notnull"` + User UserModel + AuditLog +} +type AuditLog struct { + CreatedOn time.Time `sql:"created_on"` + CreatedBy int32 `sql:"created_by"` + UpdatedOn time.Time `sql:"updated_on"` + UpdatedBy int32 `sql:"updated_by"` +} + +func (impl UserRepositoryImpl) CreateUser(userModel *UserModel, tx *pg.Tx) (*UserModel, error) { + err := tx.Insert(userModel) + if err != nil { + fmt.Println("Exception;", err) + return userModel, err + } + //TODO - Create Entry In UserRole With Default Role for User + return userModel, nil +} +func (impl UserRepositoryImpl) UpdateUser(userModel *UserModel, tx *pg.Tx) (*UserModel, error) { + err := tx.Update(userModel) + if err != nil { + fmt.Println("Exception;", err) + return userModel, err + } + + //TODO - Create Entry In UserRole With Default Role for User + + return userModel, nil +} +func (impl UserRepositoryImpl) GetById(id int32) (*UserModel, error) { + var model UserModel + err := impl.dbConnection.Model(&model).Where("id = ?", id).Where("active = ?", true).Select() + return &model, err +} +func (impl UserRepositoryImpl) GetAll() ([]UserModel, error) { + var userModel []UserModel + err := impl.dbConnection.Model(&userModel).Where("active = ?", true).Order("updated_on desc").Select() + return userModel, err +} +func (impl UserRepositoryImpl) GetUsersByFilter(size int, from int) ([]UserModel, error) { + var userModel []UserModel + /*err := impl. + dbConnection.Model(&userModel). + Column("user_model.id,user_model.email_id,user_model.access_token,"). + Order("user_model.email_id DESC"). + Offset(from). + Limit(size). + Select() + */ + query := "SELECT u.id, u.email_id, u.access_token FROM users u WHERE u.active = true" + + " ORDER by u.email_id DESC LIMIT ? OFFSET ?;" + _, err := impl.dbConnection.Query(&userModel, query, size, from) + return userModel, err +} + +func (impl UserRepositoryImpl) FetchUserByEmail(email string) (bean.UserInfo, error) { + var users bean.UserInfo + + query := "SELECT u.id, u.email_id, u.access_token FROM users u" + + " WHERE u.email_id ILIKE ? order by u.updated_on desc" + _, err := impl.dbConnection.Query(&users, query, email) + if err != nil { + impl.Logger.Error("Exception caught:", err) + return users, err + } + + return users, nil +} + +func (impl UserRepositoryImpl) FetchUserByEmailV2(email string) (*UserModel, error) { + var model UserModel + err := impl. + dbConnection.Model(&model). + Column("user_model.id,user_model.email_id,user_model.access_token,"). + Where("email_id = ?", email). + Select() + return &model, err +} +func (impl UserRepositoryImpl) FetchUserDetailByEmailV2(email string) (*UserModel, error) { + var model UserModel + err := impl.dbConnection. + Model(&model). + Column("user_model.*", "role_model"). + Join("INNER JOIN user_roles ur ON ur.user_id=user_model.id"). + Join("INNER JOIN roles r ON r.id=ur.role_id"). + Where("WHERE user_model.email_id= ?", email). + Where("user_model.active = ?", true). + Limit(1). + Select() + return &model, err +} +func (impl UserRepositoryImpl) GetByIds(ids []int32) ([]UserModel, error) { + var model []UserModel + err := impl.dbConnection.Model(&model).Where("id in (?)", pg.In(ids)).Select() + return model, err +} + +func (impl UserRepositoryImpl) DeleteUser(userModel *UserModel, tx *pg.Tx) (bool, error) { + err := tx.Delete(userModel) + if err != nil { + return false, err + } + return true, nil +} + +func (impl *UserRepositoryImpl) GetConnection() (dbConnection *pg.DB) { + return impl.dbConnection +} + +func (impl UserRepositoryImpl) GetByEmailId(email string) ([]UserModel, error) { + var model []UserModel + err := impl.dbConnection.Model(&model).Where("email_id like (?)", "%"+email+"%").Select() + return model, err +} diff --git a/image-scanner/pkg/user/UserService.go b/image-scanner/pkg/user/UserService.go new file mode 100644 index 000000000..048bb4b22 --- /dev/null +++ b/image-scanner/pkg/user/UserService.go @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package user + +import ( + "github.com/devtron-labs/image-scanner/pkg/sql/bean" + "github.com/devtron-labs/image-scanner/pkg/sql/repository" + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type UserService interface { + GetAll() ([]bean.UserInfo, error) + GetUserByEmail(emailId string) (*bean.UserInfo, error) + GetByIds(ids []int32) ([]bean.UserInfo, error) +} + +type UserServiceImpl struct { + logger *zap.SugaredLogger + userRepository repository.UserRepository +} + +func NewUserServiceImpl(logger *zap.SugaredLogger, userRepository repository.UserRepository) *UserServiceImpl { + serviceImpl := &UserServiceImpl{ + + logger: logger, + userRepository: userRepository, + } + return serviceImpl +} + +func containsArr(s []string, e string) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} + +func (impl UserServiceImpl) GetAll() ([]bean.UserInfo, error) { + model, err := impl.userRepository.GetAll() + if err != nil { + impl.logger.Errorw("error while fetching user from db", "error", err) + return nil, err + } + var response []bean.UserInfo + for _, m := range model { + response = append(response, bean.UserInfo{ + Id: m.Id, + EmailId: m.EmailId, + Groups: make([]string, 0), + }) + } + if response == nil || len(response) == 0 { + response = make([]bean.UserInfo, 0) + } + return response, nil +} + +func (impl UserServiceImpl) GetUserByEmail(emailId string) (*bean.UserInfo, error) { + model, err := impl.userRepository.FetchUserByEmail(emailId) + if err != nil { + impl.logger.Errorw("error while fetching user from db", "error", err) + return nil, err + } + response := &bean.UserInfo{ + Id: model.Id, + EmailId: model.EmailId, + AccessToken: model.AccessToken, + } + + return response, nil +} + +func (impl UserServiceImpl) GetByIds(ids []int32) ([]bean.UserInfo, error) { + var beans []bean.UserInfo + models, err := impl.userRepository.GetByIds(ids) + if err != nil && err != pg.ErrNoRows { + impl.logger.Errorw("error while fetching user from db", "error", err) + return nil, err + } + if len(models) > 0 { + for _, item := range models { + beans = append(beans, bean.UserInfo{Id: item.Id, EmailId: item.EmailId}) + } + } + return beans, nil +} diff --git a/image-scanner/pubsub/NatSubscription.go b/image-scanner/pubsub/NatSubscription.go new file mode 100644 index 000000000..7c135d230 --- /dev/null +++ b/image-scanner/pubsub/NatSubscription.go @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pubsub + +import ( + "encoding/json" + pubsub1 "github.com/devtron-labs/common-lib/pubsub-lib" + "github.com/devtron-labs/common-lib/pubsub-lib/model" + "github.com/devtron-labs/image-scanner/common" + "github.com/devtron-labs/image-scanner/pkg/clairService" + "go.uber.org/zap" +) + +type NatSubscription interface { + Subscribe() error +} + +type NatSubscriptionImpl struct { + PubSubClient *pubsub1.PubSubClientServiceImpl + Logger *zap.SugaredLogger + ClairService clairService.ClairService +} + +type NatsSubscriptionModeConfig struct { + ToBeSubscribed bool +} + +func NewNatsSubscriptionModeConfig() NatsSubscriptionModeConfig { + return NatsSubscriptionModeConfig{ + ToBeSubscribed: true, + } +} + +func NewNatSubscription(pubSubClient *pubsub1.PubSubClientServiceImpl, + logger *zap.SugaredLogger, + clairService clairService.ClairService, natsSubscriptionConfig NatsSubscriptionModeConfig) (*NatSubscriptionImpl, error) { + ns := &NatSubscriptionImpl{ + PubSubClient: pubSubClient, + Logger: logger, + ClairService: clairService, + } + + if !natsSubscriptionConfig.ToBeSubscribed { + return ns, nil + } + return ns, ns.Subscribe() +} + +func (impl *NatSubscriptionImpl) Subscribe() error { + callback := func(msg *model.PubSubMsg) { + impl.Logger.Debugw("received msg", "msg", msg) + // defer msg.Ack() + scanConfig := &common.ImageScanEvent{} + err := json.Unmarshal([]byte(msg.Data), scanConfig) + if err != nil { + impl.Logger.Errorw("err in reading msg", "err", err, "msg", string(msg.Data)) + return + } + impl.Logger.Infow("scanConfig unmarshal data", "scanConfig", scanConfig) + // NOTE: This is not being used, thats why not updated the call + // TODO: Will have to update if any usage in future + // scanConfig.Image = "quay.io/coreos/clair:v2.0.0" + _, err = impl.ClairService.ScanImage(scanConfig, nil, nil) + if err != nil { + impl.Logger.Infow("err in process msg", "err", err) + return + } + } + + var loggerFunc pubsub1.LoggerFunc = func(msg model.PubSubMsg) (string, []interface{}) { + deploymentEvent := &common.ImageScanEvent{} + err := json.Unmarshal([]byte(msg.Data), &deploymentEvent) + if err != nil { + return "error while unmarshalling deploymentEvent object", []interface{}{"err", err, "msg", msg.Data} + } + return "got message for deployment stage completion", []interface{}{"envId", deploymentEvent.EnvId, "appId", deploymentEvent.AppId, "ciArtifactId", deploymentEvent.CiArtifactId} + } + + err := impl.PubSubClient.Subscribe(pubsub1.TOPIC_CI_SCAN, callback, loggerFunc) + if err != nil { + impl.Logger.Errorw("Error while subscribing to pubsub", "topic", pubsub1.TOPIC_CI_SCAN, "error", err) + } + return err +} diff --git a/image-scanner/pubsub/TestPublish.go b/image-scanner/pubsub/TestPublish.go new file mode 100644 index 000000000..dac4f9871 --- /dev/null +++ b/image-scanner/pubsub/TestPublish.go @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pubsub + +import ( + "encoding/json" + pubsub "github.com/devtron-labs/common-lib/pubsub-lib" + "go.uber.org/zap" +) + +type TestPublish interface { + PublishForScan(channel string, payload interface{}) error +} + +type TestPublishImpl struct { + pubSubClient *pubsub.PubSubClientServiceImpl + logger *zap.SugaredLogger +} + +func NewTestPublishImpl(pubSubClient *pubsub.PubSubClientServiceImpl, + logger *zap.SugaredLogger) *TestPublishImpl { + ns := &TestPublishImpl{ + pubSubClient: pubSubClient, + logger: logger, + } + return ns +} + +func (impl *TestPublishImpl) PublishForScan(channel string, payload interface{}) error { + body, err := json.Marshal(payload) + if err != nil { + return err + } + err = impl.pubSubClient.Publish(channel, string(body)) + if err != nil { + impl.logger.Errorw("Error while publishing request", "topic", channel, "error", err) + } + return err +} diff --git a/image-scanner/tests/testTrivyScanResultV2.json b/image-scanner/tests/testTrivyScanResultV2.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/image-scanner/tests/testTrivyScanResultV2.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/image-scanner/vendor/cloud.google.com/go/compute/metadata/CHANGES.md b/image-scanner/vendor/cloud.google.com/go/compute/metadata/CHANGES.md new file mode 100644 index 000000000..967e06074 --- /dev/null +++ b/image-scanner/vendor/cloud.google.com/go/compute/metadata/CHANGES.md @@ -0,0 +1,26 @@ +# Changes + +## [0.3.0](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.2.3...compute/metadata/v0.3.0) (2024-04-15) + + +### Features + +* **compute/metadata:** Add context aware functions ([#9733](https://github.com/googleapis/google-cloud-go/issues/9733)) ([e4eb5b4](https://github.com/googleapis/google-cloud-go/commit/e4eb5b46ee2aec9d2fc18300bfd66015e25a0510)) + +## [0.2.3](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.2.2...compute/metadata/v0.2.3) (2022-12-15) + + +### Bug Fixes + +* **compute/metadata:** Switch DNS lookup to an absolute lookup ([119b410](https://github.com/googleapis/google-cloud-go/commit/119b41060c7895e45e48aee5621ad35607c4d021)), refs [#7165](https://github.com/googleapis/google-cloud-go/issues/7165) + +## [0.2.2](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.2.1...compute/metadata/v0.2.2) (2022-12-01) + + +### Bug Fixes + +* **compute/metadata:** Set IdleConnTimeout for http.Client ([#7084](https://github.com/googleapis/google-cloud-go/issues/7084)) ([766516a](https://github.com/googleapis/google-cloud-go/commit/766516aaf3816bfb3159efeea65aa3d1d205a3e2)), refs [#5430](https://github.com/googleapis/google-cloud-go/issues/5430) + +## [0.1.0] (2022-10-26) + +Initial release of metadata being it's own module. diff --git a/image-scanner/vendor/cloud.google.com/go/compute/metadata/LICENSE b/image-scanner/vendor/cloud.google.com/go/compute/metadata/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/image-scanner/vendor/cloud.google.com/go/compute/metadata/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/image-scanner/vendor/cloud.google.com/go/compute/metadata/README.md b/image-scanner/vendor/cloud.google.com/go/compute/metadata/README.md new file mode 100644 index 000000000..f940fb2c8 --- /dev/null +++ b/image-scanner/vendor/cloud.google.com/go/compute/metadata/README.md @@ -0,0 +1,27 @@ +# Compute API + +[![Go Reference](https://pkg.go.dev/badge/cloud.google.com/go/compute.svg)](https://pkg.go.dev/cloud.google.com/go/compute/metadata) + +This is a utility library for communicating with Google Cloud metadata service +on Google Cloud. + +## Install + +```bash +go get cloud.google.com/go/compute/metadata +``` + +## Go Version Support + +See the [Go Versions Supported](https://github.com/googleapis/google-cloud-go#go-versions-supported) +section in the root directory's README. + +## Contributing + +Contributions are welcome. Please, see the [CONTRIBUTING](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/main/CONTRIBUTING.md) +document for details. + +Please note that this project is released with a Contributor Code of Conduct. +By participating in this project you agree to abide by its terms. See +[Contributor Code of Conduct](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/main/CONTRIBUTING.md#contributor-code-of-conduct) +for more information. diff --git a/image-scanner/vendor/cloud.google.com/go/compute/metadata/metadata.go b/image-scanner/vendor/cloud.google.com/go/compute/metadata/metadata.go new file mode 100644 index 000000000..f67e3c7ee --- /dev/null +++ b/image-scanner/vendor/cloud.google.com/go/compute/metadata/metadata.go @@ -0,0 +1,579 @@ +// Copyright 2014 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package metadata provides access to Google Compute Engine (GCE) +// metadata and API service accounts. +// +// This package is a wrapper around the GCE metadata service, +// as documented at https://cloud.google.com/compute/docs/metadata/overview. +package metadata // import "cloud.google.com/go/compute/metadata" + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net" + "net/http" + "net/url" + "os" + "runtime" + "strings" + "sync" + "time" +) + +const ( + // metadataIP is the documented metadata server IP address. + metadataIP = "169.254.169.254" + + // metadataHostEnv is the environment variable specifying the + // GCE metadata hostname. If empty, the default value of + // metadataIP ("169.254.169.254") is used instead. + // This is variable name is not defined by any spec, as far as + // I know; it was made up for the Go package. + metadataHostEnv = "GCE_METADATA_HOST" + + userAgent = "gcloud-golang/0.1" +) + +type cachedValue struct { + k string + trim bool + mu sync.Mutex + v string +} + +var ( + projID = &cachedValue{k: "project/project-id", trim: true} + projNum = &cachedValue{k: "project/numeric-project-id", trim: true} + instID = &cachedValue{k: "instance/id", trim: true} +) + +var defaultClient = &Client{hc: newDefaultHTTPClient()} + +func newDefaultHTTPClient() *http.Client { + return &http.Client{ + Transport: &http.Transport{ + Dial: (&net.Dialer{ + Timeout: 2 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + IdleConnTimeout: 60 * time.Second, + }, + Timeout: 5 * time.Second, + } +} + +// NotDefinedError is returned when requested metadata is not defined. +// +// The underlying string is the suffix after "/computeMetadata/v1/". +// +// This error is not returned if the value is defined to be the empty +// string. +type NotDefinedError string + +func (suffix NotDefinedError) Error() string { + return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix)) +} + +func (c *cachedValue) get(cl *Client) (v string, err error) { + defer c.mu.Unlock() + c.mu.Lock() + if c.v != "" { + return c.v, nil + } + if c.trim { + v, err = cl.getTrimmed(context.Background(), c.k) + } else { + v, err = cl.GetWithContext(context.Background(), c.k) + } + if err == nil { + c.v = v + } + return +} + +var ( + onGCEOnce sync.Once + onGCE bool +) + +// OnGCE reports whether this process is running on Google Compute Engine. +func OnGCE() bool { + onGCEOnce.Do(initOnGCE) + return onGCE +} + +func initOnGCE() { + onGCE = testOnGCE() +} + +func testOnGCE() bool { + // The user explicitly said they're on GCE, so trust them. + if os.Getenv(metadataHostEnv) != "" { + return true + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + resc := make(chan bool, 2) + + // Try two strategies in parallel. + // See https://github.com/googleapis/google-cloud-go/issues/194 + go func() { + req, _ := http.NewRequest("GET", "http://"+metadataIP, nil) + req.Header.Set("User-Agent", userAgent) + res, err := newDefaultHTTPClient().Do(req.WithContext(ctx)) + if err != nil { + resc <- false + return + } + defer res.Body.Close() + resc <- res.Header.Get("Metadata-Flavor") == "Google" + }() + + go func() { + resolver := &net.Resolver{} + addrs, err := resolver.LookupHost(ctx, "metadata.google.internal.") + if err != nil || len(addrs) == 0 { + resc <- false + return + } + resc <- strsContains(addrs, metadataIP) + }() + + tryHarder := systemInfoSuggestsGCE() + if tryHarder { + res := <-resc + if res { + // The first strategy succeeded, so let's use it. + return true + } + // Wait for either the DNS or metadata server probe to + // contradict the other one and say we are running on + // GCE. Give it a lot of time to do so, since the system + // info already suggests we're running on a GCE BIOS. + timer := time.NewTimer(5 * time.Second) + defer timer.Stop() + select { + case res = <-resc: + return res + case <-timer.C: + // Too slow. Who knows what this system is. + return false + } + } + + // There's no hint from the system info that we're running on + // GCE, so use the first probe's result as truth, whether it's + // true or false. The goal here is to optimize for speed for + // users who are NOT running on GCE. We can't assume that + // either a DNS lookup or an HTTP request to a blackholed IP + // address is fast. Worst case this should return when the + // metaClient's Transport.ResponseHeaderTimeout or + // Transport.Dial.Timeout fires (in two seconds). + return <-resc +} + +// systemInfoSuggestsGCE reports whether the local system (without +// doing network requests) suggests that we're running on GCE. If this +// returns true, testOnGCE tries a bit harder to reach its metadata +// server. +func systemInfoSuggestsGCE() bool { + if runtime.GOOS != "linux" { + // We don't have any non-Linux clues available, at least yet. + return false + } + slurp, _ := os.ReadFile("/sys/class/dmi/id/product_name") + name := strings.TrimSpace(string(slurp)) + return name == "Google" || name == "Google Compute Engine" +} + +// Subscribe calls Client.SubscribeWithContext on the default client. +func Subscribe(suffix string, fn func(v string, ok bool) error) error { + return defaultClient.SubscribeWithContext(context.Background(), suffix, func(ctx context.Context, v string, ok bool) error { return fn(v, ok) }) +} + +// SubscribeWithContext calls Client.SubscribeWithContext on the default client. +func SubscribeWithContext(ctx context.Context, suffix string, fn func(ctx context.Context, v string, ok bool) error) error { + return defaultClient.SubscribeWithContext(ctx, suffix, fn) +} + +// Get calls Client.GetWithContext on the default client. +// +// Deprecated: Please use the context aware variant [GetWithContext]. +func Get(suffix string) (string, error) { + return defaultClient.GetWithContext(context.Background(), suffix) +} + +// GetWithContext calls Client.GetWithContext on the default client. +func GetWithContext(ctx context.Context, suffix string) (string, error) { + return defaultClient.GetWithContext(ctx, suffix) +} + +// ProjectID returns the current instance's project ID string. +func ProjectID() (string, error) { return defaultClient.ProjectID() } + +// NumericProjectID returns the current instance's numeric project ID. +func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() } + +// InternalIP returns the instance's primary internal IP address. +func InternalIP() (string, error) { return defaultClient.InternalIP() } + +// ExternalIP returns the instance's primary external (public) IP address. +func ExternalIP() (string, error) { return defaultClient.ExternalIP() } + +// Email calls Client.Email on the default client. +func Email(serviceAccount string) (string, error) { return defaultClient.Email(serviceAccount) } + +// Hostname returns the instance's hostname. This will be of the form +// ".c..internal". +func Hostname() (string, error) { return defaultClient.Hostname() } + +// InstanceTags returns the list of user-defined instance tags, +// assigned when initially creating a GCE instance. +func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() } + +// InstanceID returns the current VM's numeric instance ID. +func InstanceID() (string, error) { return defaultClient.InstanceID() } + +// InstanceName returns the current VM's instance ID string. +func InstanceName() (string, error) { return defaultClient.InstanceName() } + +// Zone returns the current VM's zone, such as "us-central1-b". +func Zone() (string, error) { return defaultClient.Zone() } + +// InstanceAttributes calls Client.InstanceAttributes on the default client. +func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() } + +// ProjectAttributes calls Client.ProjectAttributes on the default client. +func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() } + +// InstanceAttributeValue calls Client.InstanceAttributeValue on the default client. +func InstanceAttributeValue(attr string) (string, error) { + return defaultClient.InstanceAttributeValue(attr) +} + +// ProjectAttributeValue calls Client.ProjectAttributeValue on the default client. +func ProjectAttributeValue(attr string) (string, error) { + return defaultClient.ProjectAttributeValue(attr) +} + +// Scopes calls Client.Scopes on the default client. +func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) } + +func strsContains(ss []string, s string) bool { + for _, v := range ss { + if v == s { + return true + } + } + return false +} + +// A Client provides metadata. +type Client struct { + hc *http.Client +} + +// NewClient returns a Client that can be used to fetch metadata. +// Returns the client that uses the specified http.Client for HTTP requests. +// If nil is specified, returns the default client. +func NewClient(c *http.Client) *Client { + if c == nil { + return defaultClient + } + + return &Client{hc: c} +} + +// getETag returns a value from the metadata service as well as the associated ETag. +// This func is otherwise equivalent to Get. +func (c *Client) getETag(ctx context.Context, suffix string) (value, etag string, err error) { + // Using a fixed IP makes it very difficult to spoof the metadata service in + // a container, which is an important use-case for local testing of cloud + // deployments. To enable spoofing of the metadata service, the environment + // variable GCE_METADATA_HOST is first inspected to decide where metadata + // requests shall go. + host := os.Getenv(metadataHostEnv) + if host == "" { + // Using 169.254.169.254 instead of "metadata" here because Go + // binaries built with the "netgo" tag and without cgo won't + // know the search suffix for "metadata" is + // ".google.internal", and this IP address is documented as + // being stable anyway. + host = metadataIP + } + suffix = strings.TrimLeft(suffix, "/") + u := "http://" + host + "/computeMetadata/v1/" + suffix + req, err := http.NewRequestWithContext(ctx, "GET", u, nil) + if err != nil { + return "", "", err + } + req.Header.Set("Metadata-Flavor", "Google") + req.Header.Set("User-Agent", userAgent) + var res *http.Response + var reqErr error + retryer := newRetryer() + for { + res, reqErr = c.hc.Do(req) + var code int + if res != nil { + code = res.StatusCode + } + if delay, shouldRetry := retryer.Retry(code, reqErr); shouldRetry { + if err := sleep(ctx, delay); err != nil { + return "", "", err + } + continue + } + break + } + if reqErr != nil { + return "", "", reqErr + } + defer res.Body.Close() + if res.StatusCode == http.StatusNotFound { + return "", "", NotDefinedError(suffix) + } + all, err := io.ReadAll(res.Body) + if err != nil { + return "", "", err + } + if res.StatusCode != 200 { + return "", "", &Error{Code: res.StatusCode, Message: string(all)} + } + return string(all), res.Header.Get("Etag"), nil +} + +// Get returns a value from the metadata service. +// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". +// +// If the GCE_METADATA_HOST environment variable is not defined, a default of +// 169.254.169.254 will be used instead. +// +// If the requested metadata is not defined, the returned error will +// be of type NotDefinedError. +// +// Deprecated: Please use the context aware variant [Client.GetWithContext]. +func (c *Client) Get(suffix string) (string, error) { + return c.GetWithContext(context.Background(), suffix) +} + +// GetWithContext returns a value from the metadata service. +// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". +// +// If the GCE_METADATA_HOST environment variable is not defined, a default of +// 169.254.169.254 will be used instead. +// +// If the requested metadata is not defined, the returned error will +// be of type NotDefinedError. +func (c *Client) GetWithContext(ctx context.Context, suffix string) (string, error) { + val, _, err := c.getETag(ctx, suffix) + return val, err +} + +func (c *Client) getTrimmed(ctx context.Context, suffix string) (s string, err error) { + s, err = c.GetWithContext(ctx, suffix) + s = strings.TrimSpace(s) + return +} + +func (c *Client) lines(suffix string) ([]string, error) { + j, err := c.GetWithContext(context.Background(), suffix) + if err != nil { + return nil, err + } + s := strings.Split(strings.TrimSpace(j), "\n") + for i := range s { + s[i] = strings.TrimSpace(s[i]) + } + return s, nil +} + +// ProjectID returns the current instance's project ID string. +func (c *Client) ProjectID() (string, error) { return projID.get(c) } + +// NumericProjectID returns the current instance's numeric project ID. +func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) } + +// InstanceID returns the current VM's numeric instance ID. +func (c *Client) InstanceID() (string, error) { return instID.get(c) } + +// InternalIP returns the instance's primary internal IP address. +func (c *Client) InternalIP() (string, error) { + return c.getTrimmed(context.Background(), "instance/network-interfaces/0/ip") +} + +// Email returns the email address associated with the service account. +// The account may be empty or the string "default" to use the instance's +// main account. +func (c *Client) Email(serviceAccount string) (string, error) { + if serviceAccount == "" { + serviceAccount = "default" + } + return c.getTrimmed(context.Background(), "instance/service-accounts/"+serviceAccount+"/email") +} + +// ExternalIP returns the instance's primary external (public) IP address. +func (c *Client) ExternalIP() (string, error) { + return c.getTrimmed(context.Background(), "instance/network-interfaces/0/access-configs/0/external-ip") +} + +// Hostname returns the instance's hostname. This will be of the form +// ".c..internal". +func (c *Client) Hostname() (string, error) { + return c.getTrimmed(context.Background(), "instance/hostname") +} + +// InstanceTags returns the list of user-defined instance tags, +// assigned when initially creating a GCE instance. +func (c *Client) InstanceTags() ([]string, error) { + var s []string + j, err := c.GetWithContext(context.Background(), "instance/tags") + if err != nil { + return nil, err + } + if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil { + return nil, err + } + return s, nil +} + +// InstanceName returns the current VM's instance ID string. +func (c *Client) InstanceName() (string, error) { + return c.getTrimmed(context.Background(), "instance/name") +} + +// Zone returns the current VM's zone, such as "us-central1-b". +func (c *Client) Zone() (string, error) { + zone, err := c.getTrimmed(context.Background(), "instance/zone") + // zone is of the form "projects//zones/". + if err != nil { + return "", err + } + return zone[strings.LastIndex(zone, "/")+1:], nil +} + +// InstanceAttributes returns the list of user-defined attributes, +// assigned when initially creating a GCE VM instance. The value of an +// attribute can be obtained with InstanceAttributeValue. +func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") } + +// ProjectAttributes returns the list of user-defined attributes +// applying to the project as a whole, not just this VM. The value of +// an attribute can be obtained with ProjectAttributeValue. +func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") } + +// InstanceAttributeValue returns the value of the provided VM +// instance attribute. +// +// If the requested attribute is not defined, the returned error will +// be of type NotDefinedError. +// +// InstanceAttributeValue may return ("", nil) if the attribute was +// defined to be the empty string. +func (c *Client) InstanceAttributeValue(attr string) (string, error) { + return c.GetWithContext(context.Background(), "instance/attributes/"+attr) +} + +// ProjectAttributeValue returns the value of the provided +// project attribute. +// +// If the requested attribute is not defined, the returned error will +// be of type NotDefinedError. +// +// ProjectAttributeValue may return ("", nil) if the attribute was +// defined to be the empty string. +func (c *Client) ProjectAttributeValue(attr string) (string, error) { + return c.GetWithContext(context.Background(), "project/attributes/"+attr) +} + +// Scopes returns the service account scopes for the given account. +// The account may be empty or the string "default" to use the instance's +// main account. +func (c *Client) Scopes(serviceAccount string) ([]string, error) { + if serviceAccount == "" { + serviceAccount = "default" + } + return c.lines("instance/service-accounts/" + serviceAccount + "/scopes") +} + +// Subscribe subscribes to a value from the metadata service. +// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". +// The suffix may contain query parameters. +// +// Deprecated: Please use the context aware variant [Client.SubscribeWithContext]. +func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error { + return c.SubscribeWithContext(context.Background(), suffix, func(ctx context.Context, v string, ok bool) error { return fn(v, ok) }) +} + +// SubscribeWithContext subscribes to a value from the metadata service. +// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". +// The suffix may contain query parameters. +// +// SubscribeWithContext calls fn with the latest metadata value indicated by the +// provided suffix. If the metadata value is deleted, fn is called with the +// empty string and ok false. Subscribe blocks until fn returns a non-nil error +// or the value is deleted. Subscribe returns the error value returned from the +// last call to fn, which may be nil when ok == false. +func (c *Client) SubscribeWithContext(ctx context.Context, suffix string, fn func(ctx context.Context, v string, ok bool) error) error { + const failedSubscribeSleep = time.Second * 5 + + // First check to see if the metadata value exists at all. + val, lastETag, err := c.getETag(ctx, suffix) + if err != nil { + return err + } + + if err := fn(ctx, val, true); err != nil { + return err + } + + ok := true + if strings.ContainsRune(suffix, '?') { + suffix += "&wait_for_change=true&last_etag=" + } else { + suffix += "?wait_for_change=true&last_etag=" + } + for { + val, etag, err := c.getETag(ctx, suffix+url.QueryEscape(lastETag)) + if err != nil { + if _, deleted := err.(NotDefinedError); !deleted { + time.Sleep(failedSubscribeSleep) + continue // Retry on other errors. + } + ok = false + } + lastETag = etag + + if err := fn(ctx, val, ok); err != nil || !ok { + return err + } + } +} + +// Error contains an error response from the server. +type Error struct { + // Code is the HTTP response status code. + Code int + // Message is the server response message. + Message string +} + +func (e *Error) Error() string { + return fmt.Sprintf("compute: Received %d `%s`", e.Code, e.Message) +} diff --git a/image-scanner/vendor/cloud.google.com/go/compute/metadata/retry.go b/image-scanner/vendor/cloud.google.com/go/compute/metadata/retry.go new file mode 100644 index 000000000..3d4bc75dd --- /dev/null +++ b/image-scanner/vendor/cloud.google.com/go/compute/metadata/retry.go @@ -0,0 +1,114 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metadata + +import ( + "context" + "io" + "math/rand" + "net/http" + "time" +) + +const ( + maxRetryAttempts = 5 +) + +var ( + syscallRetryable = func(error) bool { return false } +) + +// defaultBackoff is basically equivalent to gax.Backoff without the need for +// the dependency. +type defaultBackoff struct { + max time.Duration + mul float64 + cur time.Duration +} + +func (b *defaultBackoff) Pause() time.Duration { + d := time.Duration(1 + rand.Int63n(int64(b.cur))) + b.cur = time.Duration(float64(b.cur) * b.mul) + if b.cur > b.max { + b.cur = b.max + } + return d +} + +// sleep is the equivalent of gax.Sleep without the need for the dependency. +func sleep(ctx context.Context, d time.Duration) error { + t := time.NewTimer(d) + select { + case <-ctx.Done(): + t.Stop() + return ctx.Err() + case <-t.C: + return nil + } +} + +func newRetryer() *metadataRetryer { + return &metadataRetryer{bo: &defaultBackoff{ + cur: 100 * time.Millisecond, + max: 30 * time.Second, + mul: 2, + }} +} + +type backoff interface { + Pause() time.Duration +} + +type metadataRetryer struct { + bo backoff + attempts int +} + +func (r *metadataRetryer) Retry(status int, err error) (time.Duration, bool) { + if status == http.StatusOK { + return 0, false + } + retryOk := shouldRetry(status, err) + if !retryOk { + return 0, false + } + if r.attempts == maxRetryAttempts { + return 0, false + } + r.attempts++ + return r.bo.Pause(), true +} + +func shouldRetry(status int, err error) bool { + if 500 <= status && status <= 599 { + return true + } + if err == io.ErrUnexpectedEOF { + return true + } + // Transient network errors should be retried. + if syscallRetryable(err) { + return true + } + if err, ok := err.(interface{ Temporary() bool }); ok { + if err.Temporary() { + return true + } + } + if err, ok := err.(interface{ Unwrap() error }); ok { + return shouldRetry(status, err.Unwrap()) + } + return false +} diff --git a/image-scanner/vendor/cloud.google.com/go/compute/metadata/retry_linux.go b/image-scanner/vendor/cloud.google.com/go/compute/metadata/retry_linux.go new file mode 100644 index 000000000..bb412f891 --- /dev/null +++ b/image-scanner/vendor/cloud.google.com/go/compute/metadata/retry_linux.go @@ -0,0 +1,26 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build linux +// +build linux + +package metadata + +import "syscall" + +func init() { + // Initialize syscallRetryable to return true on transient socket-level + // errors. These errors are specific to Linux. + syscallRetryable = func(err error) bool { return err == syscall.ECONNRESET || err == syscall.ECONNREFUSED } +} diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/.gitignore b/image-scanner/vendor/github.com/Knetic/govaluate/.gitignore new file mode 100644 index 000000000..da210fb31 --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/.gitignore @@ -0,0 +1,28 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +coverage.out + +manual_test.go +*.out +*.err diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/.travis.yml b/image-scanner/vendor/github.com/Knetic/govaluate/.travis.yml new file mode 100644 index 000000000..35ae404ab --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/.travis.yml @@ -0,0 +1,10 @@ +language: go + +script: ./test.sh + +go: + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - 1.6 diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/CONTRIBUTORS b/image-scanner/vendor/github.com/Knetic/govaluate/CONTRIBUTORS new file mode 100644 index 000000000..c1a7fe42d --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/CONTRIBUTORS @@ -0,0 +1,15 @@ +This library was authored by George Lester, and contains contributions from: + +vjeantet (regex support) +iasci (ternary operator) +oxtoacart (parameter structures, deferred parameter retrieval) +wmiller848 (bitwise operators) +prashantv (optimization of bools) +dpaolella (exposure of variables used in an expression) +benpaxton (fix for missing type checks during literal elide process) +abrander (panic-finding testing tool, float32 conversions) +xfennec (fix for dates being parsed in the current Location) +bgaifullin (lifting restriction on complex/struct types) +gautambt (hexadecimal literals) +felixonmars (fix multiple typos in test names) +sambonfire (automatic type conversion for accessor function calls) \ No newline at end of file diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/EvaluableExpression.go b/image-scanner/vendor/github.com/Knetic/govaluate/EvaluableExpression.go new file mode 100644 index 000000000..a5fe50d47 --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/EvaluableExpression.go @@ -0,0 +1,276 @@ +package govaluate + +import ( + "errors" + "fmt" +) + +const isoDateFormat string = "2006-01-02T15:04:05.999999999Z0700" +const shortCircuitHolder int = -1 + +var DUMMY_PARAMETERS = MapParameters(map[string]interface{}{}) + +/* + EvaluableExpression represents a set of ExpressionTokens which, taken together, + are an expression that can be evaluated down into a single value. +*/ +type EvaluableExpression struct { + + /* + Represents the query format used to output dates. Typically only used when creating SQL or Mongo queries from an expression. + Defaults to the complete ISO8601 format, including nanoseconds. + */ + QueryDateFormat string + + /* + Whether or not to safely check types when evaluating. + If true, this library will return error messages when invalid types are used. + If false, the library will panic when operators encounter types they can't use. + + This is exclusively for users who need to squeeze every ounce of speed out of the library as they can, + and you should only set this to false if you know exactly what you're doing. + */ + ChecksTypes bool + + tokens []ExpressionToken + evaluationStages *evaluationStage + inputExpression string +} + +/* + Parses a new EvaluableExpression from the given [expression] string. + Returns an error if the given expression has invalid syntax. +*/ +func NewEvaluableExpression(expression string) (*EvaluableExpression, error) { + + functions := make(map[string]ExpressionFunction) + return NewEvaluableExpressionWithFunctions(expression, functions) +} + +/* + Similar to [NewEvaluableExpression], except that instead of a string, an already-tokenized expression is given. + This is useful in cases where you may be generating an expression automatically, or using some other parser (e.g., to parse from a query language) +*/ +func NewEvaluableExpressionFromTokens(tokens []ExpressionToken) (*EvaluableExpression, error) { + + var ret *EvaluableExpression + var err error + + ret = new(EvaluableExpression) + ret.QueryDateFormat = isoDateFormat + + err = checkBalance(tokens) + if err != nil { + return nil, err + } + + err = checkExpressionSyntax(tokens) + if err != nil { + return nil, err + } + + ret.tokens, err = optimizeTokens(tokens) + if err != nil { + return nil, err + } + + ret.evaluationStages, err = planStages(ret.tokens) + if err != nil { + return nil, err + } + + ret.ChecksTypes = true + return ret, nil +} + +/* + Similar to [NewEvaluableExpression], except enables the use of user-defined functions. + Functions passed into this will be available to the expression. +*/ +func NewEvaluableExpressionWithFunctions(expression string, functions map[string]ExpressionFunction) (*EvaluableExpression, error) { + + var ret *EvaluableExpression + var err error + + ret = new(EvaluableExpression) + ret.QueryDateFormat = isoDateFormat + ret.inputExpression = expression + + ret.tokens, err = parseTokens(expression, functions) + if err != nil { + return nil, err + } + + err = checkBalance(ret.tokens) + if err != nil { + return nil, err + } + + err = checkExpressionSyntax(ret.tokens) + if err != nil { + return nil, err + } + + ret.tokens, err = optimizeTokens(ret.tokens) + if err != nil { + return nil, err + } + + ret.evaluationStages, err = planStages(ret.tokens) + if err != nil { + return nil, err + } + + ret.ChecksTypes = true + return ret, nil +} + +/* + Same as `Eval`, but automatically wraps a map of parameters into a `govalute.Parameters` structure. +*/ +func (this EvaluableExpression) Evaluate(parameters map[string]interface{}) (interface{}, error) { + + if parameters == nil { + return this.Eval(nil) + } + + return this.Eval(MapParameters(parameters)) +} + +/* + Runs the entire expression using the given [parameters]. + e.g., If the expression contains a reference to the variable "foo", it will be taken from `parameters.Get("foo")`. + + This function returns errors if the combination of expression and parameters cannot be run, + such as if a variable in the expression is not present in [parameters]. + + In all non-error circumstances, this returns the single value result of the expression and parameters given. + e.g., if the expression is "1 + 1", this will return 2.0. + e.g., if the expression is "foo + 1" and parameters contains "foo" = 2, this will return 3.0 +*/ +func (this EvaluableExpression) Eval(parameters Parameters) (interface{}, error) { + + if this.evaluationStages == nil { + return nil, nil + } + + if parameters != nil { + parameters = &sanitizedParameters{parameters} + } else { + parameters = DUMMY_PARAMETERS + } + + return this.evaluateStage(this.evaluationStages, parameters) +} + +func (this EvaluableExpression) evaluateStage(stage *evaluationStage, parameters Parameters) (interface{}, error) { + + var left, right interface{} + var err error + + if stage.leftStage != nil { + left, err = this.evaluateStage(stage.leftStage, parameters) + if err != nil { + return nil, err + } + } + + if stage.isShortCircuitable() { + switch stage.symbol { + case AND: + if left == false { + return false, nil + } + case OR: + if left == true { + return true, nil + } + case COALESCE: + if left != nil { + return left, nil + } + + case TERNARY_TRUE: + if left == false { + right = shortCircuitHolder + } + case TERNARY_FALSE: + if left != nil { + right = shortCircuitHolder + } + } + } + + if right != shortCircuitHolder && stage.rightStage != nil { + right, err = this.evaluateStage(stage.rightStage, parameters) + if err != nil { + return nil, err + } + } + + if this.ChecksTypes { + if stage.typeCheck == nil { + + err = typeCheck(stage.leftTypeCheck, left, stage.symbol, stage.typeErrorFormat) + if err != nil { + return nil, err + } + + err = typeCheck(stage.rightTypeCheck, right, stage.symbol, stage.typeErrorFormat) + if err != nil { + return nil, err + } + } else { + // special case where the type check needs to know both sides to determine if the operator can handle it + if !stage.typeCheck(left, right) { + errorMsg := fmt.Sprintf(stage.typeErrorFormat, left, stage.symbol.String()) + return nil, errors.New(errorMsg) + } + } + } + + return stage.operator(left, right, parameters) +} + +func typeCheck(check stageTypeCheck, value interface{}, symbol OperatorSymbol, format string) error { + + if check == nil { + return nil + } + + if check(value) { + return nil + } + + errorMsg := fmt.Sprintf(format, value, symbol.String()) + return errors.New(errorMsg) +} + +/* + Returns an array representing the ExpressionTokens that make up this expression. +*/ +func (this EvaluableExpression) Tokens() []ExpressionToken { + + return this.tokens +} + +/* + Returns the original expression used to create this EvaluableExpression. +*/ +func (this EvaluableExpression) String() string { + + return this.inputExpression +} + +/* + Returns an array representing the variables contained in this EvaluableExpression. +*/ +func (this EvaluableExpression) Vars() []string { + var varlist []string + for _, val := range this.Tokens() { + if val.Kind == VARIABLE { + varlist = append(varlist, val.Value.(string)) + } + } + return varlist +} diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/EvaluableExpression_sql.go b/image-scanner/vendor/github.com/Knetic/govaluate/EvaluableExpression_sql.go new file mode 100644 index 000000000..7e0ad1c88 --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/EvaluableExpression_sql.go @@ -0,0 +1,167 @@ +package govaluate + +import ( + "errors" + "fmt" + "regexp" + "time" +) + +/* + Returns a string representing this expression as if it were written in SQL. + This function assumes that all parameters exist within the same table, and that the table essentially represents + a serialized object of some sort (e.g., hibernate). + If your data model is more normalized, you may need to consider iterating through each actual token given by `Tokens()` + to create your query. + + Boolean values are considered to be "1" for true, "0" for false. + + Times are formatted according to this.QueryDateFormat. +*/ +func (this EvaluableExpression) ToSQLQuery() (string, error) { + + var stream *tokenStream + var transactions *expressionOutputStream + var transaction string + var err error + + stream = newTokenStream(this.tokens) + transactions = new(expressionOutputStream) + + for stream.hasNext() { + + transaction, err = this.findNextSQLString(stream, transactions) + if err != nil { + return "", err + } + + transactions.add(transaction) + } + + return transactions.createString(" "), nil +} + +func (this EvaluableExpression) findNextSQLString(stream *tokenStream, transactions *expressionOutputStream) (string, error) { + + var token ExpressionToken + var ret string + + token = stream.next() + + switch token.Kind { + + case STRING: + ret = fmt.Sprintf("'%v'", token.Value) + case PATTERN: + ret = fmt.Sprintf("'%s'", token.Value.(*regexp.Regexp).String()) + case TIME: + ret = fmt.Sprintf("'%s'", token.Value.(time.Time).Format(this.QueryDateFormat)) + + case LOGICALOP: + switch logicalSymbols[token.Value.(string)] { + + case AND: + ret = "AND" + case OR: + ret = "OR" + } + + case BOOLEAN: + if token.Value.(bool) { + ret = "1" + } else { + ret = "0" + } + + case VARIABLE: + ret = fmt.Sprintf("[%s]", token.Value.(string)) + + case NUMERIC: + ret = fmt.Sprintf("%g", token.Value.(float64)) + + case COMPARATOR: + switch comparatorSymbols[token.Value.(string)] { + + case EQ: + ret = "=" + case NEQ: + ret = "<>" + case REQ: + ret = "RLIKE" + case NREQ: + ret = "NOT RLIKE" + default: + ret = fmt.Sprintf("%s", token.Value.(string)) + } + + case TERNARY: + + switch ternarySymbols[token.Value.(string)] { + + case COALESCE: + + left := transactions.rollback() + right, err := this.findNextSQLString(stream, transactions) + if err != nil { + return "", err + } + + ret = fmt.Sprintf("COALESCE(%v, %v)", left, right) + case TERNARY_TRUE: + fallthrough + case TERNARY_FALSE: + return "", errors.New("Ternary operators are unsupported in SQL output") + } + case PREFIX: + switch prefixSymbols[token.Value.(string)] { + + case INVERT: + ret = fmt.Sprintf("NOT") + default: + + right, err := this.findNextSQLString(stream, transactions) + if err != nil { + return "", err + } + + ret = fmt.Sprintf("%s%s", token.Value.(string), right) + } + case MODIFIER: + + switch modifierSymbols[token.Value.(string)] { + + case EXPONENT: + + left := transactions.rollback() + right, err := this.findNextSQLString(stream, transactions) + if err != nil { + return "", err + } + + ret = fmt.Sprintf("POW(%s, %s)", left, right) + case MODULUS: + + left := transactions.rollback() + right, err := this.findNextSQLString(stream, transactions) + if err != nil { + return "", err + } + + ret = fmt.Sprintf("MOD(%s, %s)", left, right) + default: + ret = fmt.Sprintf("%s", token.Value.(string)) + } + case CLAUSE: + ret = "(" + case CLAUSE_CLOSE: + ret = ")" + case SEPARATOR: + ret = "," + + default: + errorMsg := fmt.Sprintf("Unrecognized query token '%s' of kind '%s'", token.Value, token.Kind) + return "", errors.New(errorMsg) + } + + return ret, nil +} diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/ExpressionToken.go b/image-scanner/vendor/github.com/Knetic/govaluate/ExpressionToken.go new file mode 100644 index 000000000..f849f3813 --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/ExpressionToken.go @@ -0,0 +1,9 @@ +package govaluate + +/* + Represents a single parsed token. +*/ +type ExpressionToken struct { + Kind TokenKind + Value interface{} +} diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/LICENSE b/image-scanner/vendor/github.com/Knetic/govaluate/LICENSE new file mode 100644 index 000000000..24b9b4591 --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2016 George Lester + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/MANUAL.md b/image-scanner/vendor/github.com/Knetic/govaluate/MANUAL.md new file mode 100644 index 000000000..e06582851 --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/MANUAL.md @@ -0,0 +1,176 @@ +govaluate +==== + +This library contains quite a lot of functionality, this document is meant to be formal documentation on the operators and features of it. +Some of this documentation may duplicate what's in README.md, but should never conflict. + +# Types + +This library only officially deals with four types; `float64`, `bool`, `string`, and arrays. + +All numeric literals, with or without a radix, will be converted to `float64` for evaluation. For instance; in practice, there is no difference between the literals "1.0" and "1", they both end up as `float64`. This matters to users because if you intend to return numeric values from your expressions, then the returned value will be `float64`, not any other numeric type. + +Any string _literal_ (not parameter) which is interpretable as a date will be converted to a `float64` representation of that date's unix time. Any `time.Time` parameters will not be operable with these date literals; such parameters will need to use the `time.Time.Unix()` method to get a numeric representation. + +Arrays are untyped, and can be mixed-type. Internally they're all just `interface{}`. Only two operators can interact with arrays, `IN` and `,`. All other operators will refuse to operate on arrays. + +# Operators + +## Modifiers + +### Addition, concatenation `+` + +If either left or right sides of the `+` operator are a `string`, then this operator will perform string concatenation and return that result. If neither are string, then both must be numeric, and this will return a numeric result. + +Any other case is invalid. + +### Arithmetic `-` `*` `/` `**` `%` + +`**` refers to "take to the power of". For instance, `3 ** 4` == 81. + +* _Left side_: numeric +* _Right side_: numeric +* _Returns_: numeric + +### Bitwise shifts, masks `>>` `<<` `|` `&` `^` + +All of these operators convert their `float64` left and right sides to `int64`, perform their operation, and then convert back. +Given how this library assumes numeric are represented (as `float64`), it is unlikely that this behavior will change, even though it may cause havoc with extremely large or small numbers. + +* _Left side_: numeric +* _Right side_: numeric +* _Returns_: numeric + +### Negation `-` + +Prefix only. This can never have a left-hand value. + +* _Right side_: numeric +* _Returns_: numeric + +### Inversion `!` + +Prefix only. This can never have a left-hand value. + +* _Right side_: bool +* _Returns_: bool + +### Bitwise NOT `~` + +Prefix only. This can never have a left-hand value. + +* _Right side_: numeric +* _Returns_: numeric + +## Logical Operators + +For all logical operators, this library will short-circuit the operation if the left-hand side is sufficient to determine what to do. For instance, `true || expensiveOperation()` will not actually call `expensiveOperation()`, since it knows the left-hand side is `true`. + +### Logical AND/OR `&&` `||` + +* _Left side_: bool +* _Right side_: bool +* _Returns_: bool + +### Ternary true `?` + +Checks if the left side is `true`. If so, returns the right side. If the left side is `false`, returns `nil`. +In practice, this is commonly used with the other ternary operator. + +* _Left side_: bool +* _Right side_: Any type. +* _Returns_: Right side or `nil` + +### Ternary false `:` + +Checks if the left side is `nil`. If so, returns the right side. If the left side is non-nil, returns the left side. +In practice, this is commonly used with the other ternary operator. + +* _Left side_: Any type. +* _Right side_: Any type. +* _Returns_: Right side or `nil` + +### Null coalescence `??` + +Similar to the C# operator. If the left value is non-nil, it returns that. If not, then the right-value is returned. + +* _Left side_: Any type. +* _Right side_: Any type. +* _Returns_: No specific type - whichever is passed to it. + +## Comparators + +### Numeric/lexicographic comparators `>` `<` `>=` `<=` + +If both sides are numeric, this returns the usual greater/lesser behavior that would be expected. +If both sides are string, this returns the lexicographic comparison of the strings. This uses Go's standard lexicographic compare. + +* _Accepts_: Left and right side must either be both string, or both numeric. +* _Returns_: bool + +### Regex comparators `=~` `!~` + +These use go's standard `regexp` flavor of regex. The left side is expected to be the candidate string, the right side is the pattern. `=~` returns whether or not the candidate string matches the regex pattern given on the right. `!~` is the inverted version of the same logic. + +* _Left side_: string +* _Right side_: string +* _Returns_: bool + +## Arrays + +### Separator `,` + +The separator, always paired with parenthesis, creates arrays. It must always have both a left and right-hand value, so for instance `(, 0)` and `(0,)` are invalid uses of it. + +Again, this should always be used with parenthesis; like `(1, 2, 3, 4)`. + +### Membership `IN` + +The only operator with a text name, this operator checks the right-hand side array to see if it contains a value that is equal to the left-side value. +Equality is determined by the use of the `==` operator, and this library doesn't check types between the values. Any two values, when cast to `interface{}`, and can still be checked for equality with `==` will act as expected. + +Note that you can use a parameter for the array, but it must be an `[]interface{}`. + +* _Left side_: Any type. +* _Right side_: array +* _Returns_: bool + +# Parameters + +Parameters must be passed in every time the expression is evaluated. Parameters can be of any type, but will not cause errors unless actually used in an erroneous way. There is no difference in behavior for any of the above operators for parameters - they are type checked when used. + +All `int` and `float` values of any width will be converted to `float64` before use. + +At no point is the parameter structure, or any value thereof, modified by this library. + +## Alternates to maps + +The default form of parameters as a map may not serve your use case. You may have parameters in some other structure, you may want to change the no-parameter-found behavior, or maybe even just have some debugging print statements invoked when a parameter is accessed. + +To do this, define a type that implements the `govaluate.Parameters` interface. When you want to evaluate, instead call `EvaluableExpression.Eval` and pass your parameter structure. + +# Functions + +During expression parsing (_not_ evaluation), a map of functions can be given to `govaluate.NewEvaluableExpressionWithFunctions` (the lengthiest and finest of function names). The resultant expression will be able to invoke those functions during evaluation. Once parsed, an expression cannot have functions added or removed - a new expression will need to be created if you want to change the functions, or behavior of said functions. + +Functions always take the form `()`, including parens. Functions can have an empty list of parameters, like `()`, but still must have parens. + +If the expression contains something that looks like it ought to be a function (such as `foo()`), but no such function was given to it, it will error on parsing. + +Functions must be of type `map[string]govaluate.ExpressionFunction`. `ExpressionFunction`, for brevity, has the following signature: + +`func(args ...interface{}) (interface{}, error)` + +Where `args` is whatever is passed to the function when called. If a non-nil error is returned from a function during evaluation, the evaluation stops and ultimately returns that error to the caller of `Evaluate()` or `Eval()`. + +## Built-in functions + +There aren't any builtin functions. The author is opposed to maintaining a standard library of functions to be used. + +Every use case of this library is different, and even in simple use cases (such as parameters, see above) different users need different behavior, naming, or even functionality. The author prefers that users make their own decisions about what functions they need, and how they operate. + +# Equality + +The `==` and `!=` operators involve a moderately complex workflow. They use [`reflect.DeepEqual`](https://golang.org/pkg/reflect/#DeepEqual). This is for complicated reasons, but there are some types in Go that cannot be compared with the native `==` operator. Arrays, in particular, cannot be compared - Go will panic if you try. One might assume this could be handled with the type checking system in `govaluate`, but unfortunately without reflection there is no way to know if a variable is a slice/array. Worse, structs can be incomparable if they _contain incomparable types_. + +It's all very complicated. Fortunately, Go includes the `reflect.DeepEqual` function to handle all the edge cases. Currently, `govaluate` uses that for all equality/inequality. diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/OperatorSymbol.go b/image-scanner/vendor/github.com/Knetic/govaluate/OperatorSymbol.go new file mode 100644 index 000000000..4b810658b --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/OperatorSymbol.go @@ -0,0 +1,309 @@ +package govaluate + +/* + Represents the valid symbols for operators. + +*/ +type OperatorSymbol int + +const ( + VALUE OperatorSymbol = iota + LITERAL + NOOP + EQ + NEQ + GT + LT + GTE + LTE + REQ + NREQ + IN + + AND + OR + + PLUS + MINUS + BITWISE_AND + BITWISE_OR + BITWISE_XOR + BITWISE_LSHIFT + BITWISE_RSHIFT + MULTIPLY + DIVIDE + MODULUS + EXPONENT + + NEGATE + INVERT + BITWISE_NOT + + TERNARY_TRUE + TERNARY_FALSE + COALESCE + + FUNCTIONAL + ACCESS + SEPARATE +) + +type operatorPrecedence int + +const ( + noopPrecedence operatorPrecedence = iota + valuePrecedence + functionalPrecedence + prefixPrecedence + exponentialPrecedence + additivePrecedence + bitwisePrecedence + bitwiseShiftPrecedence + multiplicativePrecedence + comparatorPrecedence + ternaryPrecedence + logicalAndPrecedence + logicalOrPrecedence + separatePrecedence +) + +func findOperatorPrecedenceForSymbol(symbol OperatorSymbol) operatorPrecedence { + + switch symbol { + case NOOP: + return noopPrecedence + case VALUE: + return valuePrecedence + case EQ: + fallthrough + case NEQ: + fallthrough + case GT: + fallthrough + case LT: + fallthrough + case GTE: + fallthrough + case LTE: + fallthrough + case REQ: + fallthrough + case NREQ: + fallthrough + case IN: + return comparatorPrecedence + case AND: + return logicalAndPrecedence + case OR: + return logicalOrPrecedence + case BITWISE_AND: + fallthrough + case BITWISE_OR: + fallthrough + case BITWISE_XOR: + return bitwisePrecedence + case BITWISE_LSHIFT: + fallthrough + case BITWISE_RSHIFT: + return bitwiseShiftPrecedence + case PLUS: + fallthrough + case MINUS: + return additivePrecedence + case MULTIPLY: + fallthrough + case DIVIDE: + fallthrough + case MODULUS: + return multiplicativePrecedence + case EXPONENT: + return exponentialPrecedence + case BITWISE_NOT: + fallthrough + case NEGATE: + fallthrough + case INVERT: + return prefixPrecedence + case COALESCE: + fallthrough + case TERNARY_TRUE: + fallthrough + case TERNARY_FALSE: + return ternaryPrecedence + case ACCESS: + fallthrough + case FUNCTIONAL: + return functionalPrecedence + case SEPARATE: + return separatePrecedence + } + + return valuePrecedence +} + +/* + Map of all valid comparators, and their string equivalents. + Used during parsing of expressions to determine if a symbol is, in fact, a comparator. + Also used during evaluation to determine exactly which comparator is being used. +*/ +var comparatorSymbols = map[string]OperatorSymbol{ + "==": EQ, + "!=": NEQ, + ">": GT, + ">=": GTE, + "<": LT, + "<=": LTE, + "=~": REQ, + "!~": NREQ, + "in": IN, +} + +var logicalSymbols = map[string]OperatorSymbol{ + "&&": AND, + "||": OR, +} + +var bitwiseSymbols = map[string]OperatorSymbol{ + "^": BITWISE_XOR, + "&": BITWISE_AND, + "|": BITWISE_OR, +} + +var bitwiseShiftSymbols = map[string]OperatorSymbol{ + ">>": BITWISE_RSHIFT, + "<<": BITWISE_LSHIFT, +} + +var additiveSymbols = map[string]OperatorSymbol{ + "+": PLUS, + "-": MINUS, +} + +var multiplicativeSymbols = map[string]OperatorSymbol{ + "*": MULTIPLY, + "/": DIVIDE, + "%": MODULUS, +} + +var exponentialSymbolsS = map[string]OperatorSymbol{ + "**": EXPONENT, +} + +var prefixSymbols = map[string]OperatorSymbol{ + "-": NEGATE, + "!": INVERT, + "~": BITWISE_NOT, +} + +var ternarySymbols = map[string]OperatorSymbol{ + "?": TERNARY_TRUE, + ":": TERNARY_FALSE, + "??": COALESCE, +} + +// this is defined separately from additiveSymbols et al because it's needed for parsing, not stage planning. +var modifierSymbols = map[string]OperatorSymbol{ + "+": PLUS, + "-": MINUS, + "*": MULTIPLY, + "/": DIVIDE, + "%": MODULUS, + "**": EXPONENT, + "&": BITWISE_AND, + "|": BITWISE_OR, + "^": BITWISE_XOR, + ">>": BITWISE_RSHIFT, + "<<": BITWISE_LSHIFT, +} + +var separatorSymbols = map[string]OperatorSymbol{ + ",": SEPARATE, +} + +/* + Returns true if this operator is contained by the given array of candidate symbols. + False otherwise. +*/ +func (this OperatorSymbol) IsModifierType(candidate []OperatorSymbol) bool { + + for _, symbolType := range candidate { + if this == symbolType { + return true + } + } + + return false +} + +/* + Generally used when formatting type check errors. + We could store the stringified symbol somewhere else and not require a duplicated codeblock to translate + OperatorSymbol to string, but that would require more memory, and another field somewhere. + Adding operators is rare enough that we just stringify it here instead. +*/ +func (this OperatorSymbol) String() string { + + switch this { + case NOOP: + return "NOOP" + case VALUE: + return "VALUE" + case EQ: + return "=" + case NEQ: + return "!=" + case GT: + return ">" + case LT: + return "<" + case GTE: + return ">=" + case LTE: + return "<=" + case REQ: + return "=~" + case NREQ: + return "!~" + case AND: + return "&&" + case OR: + return "||" + case IN: + return "in" + case BITWISE_AND: + return "&" + case BITWISE_OR: + return "|" + case BITWISE_XOR: + return "^" + case BITWISE_LSHIFT: + return "<<" + case BITWISE_RSHIFT: + return ">>" + case PLUS: + return "+" + case MINUS: + return "-" + case MULTIPLY: + return "*" + case DIVIDE: + return "/" + case MODULUS: + return "%" + case EXPONENT: + return "**" + case NEGATE: + return "-" + case INVERT: + return "!" + case BITWISE_NOT: + return "~" + case TERNARY_TRUE: + return "?" + case TERNARY_FALSE: + return ":" + case COALESCE: + return "??" + } + return "" +} diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/README.md b/image-scanner/vendor/github.com/Knetic/govaluate/README.md new file mode 100644 index 000000000..2e5716d4f --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/README.md @@ -0,0 +1,233 @@ +govaluate +==== + +[![Build Status](https://travis-ci.org/Knetic/govaluate.svg?branch=master)](https://travis-ci.org/Knetic/govaluate) +[![Godoc](https://img.shields.io/badge/godoc-reference-5272B4.svg)](https://godoc.org/github.com/Knetic/govaluate) +[![Go Report Card](https://goreportcard.com/badge/github.com/Knetic/govaluate)](https://goreportcard.com/report/github.com/Knetic/govaluate) +[![Gocover](https://gocover.io/_badge/github.com/Knetic/govaluate)](https://gocover.io/github.com/Knetic/govaluate) + +Provides support for evaluating arbitrary C-like artithmetic/string expressions. + +Why can't you just write these expressions in code? +-- + +Sometimes, you can't know ahead-of-time what an expression will look like, or you want those expressions to be configurable. +Perhaps you've got a set of data running through your application, and you want to allow your users to specify some validations to run on it before committing it to a database. Or maybe you've written a monitoring framework which is capable of gathering a bunch of metrics, then evaluating a few expressions to see if any metrics should be alerted upon, but the conditions for alerting are different for each monitor. + +A lot of people wind up writing their own half-baked style of evaluation language that fits their needs, but isn't complete. Or they wind up baking the expression into the actual executable, even if they know it's subject to change. These strategies may work, but they take time to implement, time for users to learn, and induce technical debt as requirements change. This library is meant to cover all the normal C-like expressions, so that you don't have to reinvent one of the oldest wheels on a computer. + +How do I use it? +-- + +You create a new EvaluableExpression, then call "Evaluate" on it. + +```go + expression, err := govaluate.NewEvaluableExpression("10 > 0"); + result, err := expression.Evaluate(nil); + // result is now set to "true", the bool value. +``` + +Cool, but how about with parameters? + +```go + expression, err := govaluate.NewEvaluableExpression("foo > 0"); + + parameters := make(map[string]interface{}, 8) + parameters["foo"] = -1; + + result, err := expression.Evaluate(parameters); + // result is now set to "false", the bool value. +``` + +That's cool, but we can almost certainly have done all that in code. What about a complex use case that involves some math? + +```go + expression, err := govaluate.NewEvaluableExpression("(requests_made * requests_succeeded / 100) >= 90"); + + parameters := make(map[string]interface{}, 8) + parameters["requests_made"] = 100; + parameters["requests_succeeded"] = 80; + + result, err := expression.Evaluate(parameters); + // result is now set to "false", the bool value. +``` + +Or maybe you want to check the status of an alive check ("smoketest") page, which will be a string? + +```go + expression, err := govaluate.NewEvaluableExpression("http_response_body == 'service is ok'"); + + parameters := make(map[string]interface{}, 8) + parameters["http_response_body"] = "service is ok"; + + result, err := expression.Evaluate(parameters); + // result is now set to "true", the bool value. +``` + +These examples have all returned boolean values, but it's equally possible to return numeric ones. + +```go + expression, err := govaluate.NewEvaluableExpression("(mem_used / total_mem) * 100"); + + parameters := make(map[string]interface{}, 8) + parameters["total_mem"] = 1024; + parameters["mem_used"] = 512; + + result, err := expression.Evaluate(parameters); + // result is now set to "50.0", the float64 value. +``` + +You can also do date parsing, though the formats are somewhat limited. Stick to RF3339, ISO8061, unix date, or ruby date formats. If you're having trouble getting a date string to parse, check the list of formats actually used: [parsing.go:248](https://github.com/Knetic/govaluate/blob/0580e9b47a69125afa0e4ebd1cf93c49eb5a43ec/parsing.go#L258). + +```go + expression, err := govaluate.NewEvaluableExpression("'2014-01-02' > '2014-01-01 23:59:59'"); + result, err := expression.Evaluate(nil); + + // result is now set to true +``` + +Expressions are parsed once, and can be re-used multiple times. Parsing is the compute-intensive phase of the process, so if you intend to use the same expression with different parameters, just parse it once. Like so; + +```go + expression, err := govaluate.NewEvaluableExpression("response_time <= 100"); + parameters := make(map[string]interface{}, 8) + + for { + parameters["response_time"] = pingSomething(); + result, err := expression.Evaluate(parameters) + } +``` + +The normal C-standard order of operators is respected. When writing an expression, be sure that you either order the operators correctly, or use parenthesis to clarify which portions of an expression should be run first. + +Escaping characters +-- + +Sometimes you'll have parameters that have spaces, slashes, pluses, ampersands or some other character +that this library interprets as something special. For example, the following expression will not +act as one might expect: + + "response-time < 100" + +As written, the library will parse it as "[response] minus [time] is less than 100". In reality, +"response-time" is meant to be one variable that just happens to have a dash in it. + +There are two ways to work around this. First, you can escape the entire parameter name: + + "[response-time] < 100" + +Or you can use backslashes to escape only the minus sign. + + "response\\-time < 100" + +Backslashes can be used anywhere in an expression to escape the very next character. Square bracketed parameter names can be used instead of plain parameter names at any time. + +Functions +-- + +You may have cases where you want to call a function on a parameter during execution of the expression. Perhaps you want to aggregate some set of data, but don't know the exact aggregation you want to use until you're writing the expression itself. Or maybe you have a mathematical operation you want to perform, for which there is no operator; like `log` or `tan` or `sqrt`. For cases like this, you can provide a map of functions to `NewEvaluableExpressionWithFunctions`, which will then be able to use them during execution. For instance; + +```go + functions := map[string]govaluate.ExpressionFunction { + "strlen": func(args ...interface{}) (interface{}, error) { + length := len(args[0].(string)) + return (float64)(length), nil + }, + } + + expString := "strlen('someReallyLongInputString') <= 16" + expression, _ := govaluate.NewEvaluableExpressionWithFunctions(expString, functions) + + result, _ := expression.Evaluate(nil) + // result is now "false", the boolean value +``` + +Functions can accept any number of arguments, correctly handles nested functions, and arguments can be of any type (even if none of this library's operators support evaluation of that type). For instance, each of these usages of functions in an expression are valid (assuming that the appropriate functions and parameters are given): + +```go +"sqrt(x1 ** y1, x2 ** y2)" +"max(someValue, abs(anotherValue), 10 * lastValue)" +``` + +Functions cannot be passed as parameters, they must be known at the time when the expression is parsed, and are unchangeable after parsing. + +Accessors +-- + +If you have structs in your parameters, you can access their fields and methods in the usual way. For instance, given a struct that has a method "Echo", present in the parameters as `foo`, the following is valid: + + "foo.Echo('hello world')" + +Fields are accessed in a similar way. Assuming `foo` has a field called "Length": + + "foo.Length > 9000" + +Accessors can be nested to any depth, like the following + + "foo.Bar.Baz.SomeFunction()" + +However it is not _currently_ supported to access values in `map`s. So the following will not work + + "foo.SomeMap['key']" + +This may be convenient, but note that using accessors involves a _lot_ of reflection. This makes the expression about four times slower than just using a parameter (consult the benchmarks for more precise measurements on your system). +If at all reasonable, the author recommends extracting the values you care about into a parameter map beforehand, or defining a struct that implements the `Parameters` interface, and which grabs fields as required. If there are functions you want to use, it's better to pass them as expression functions (see the above section). These approaches use no reflection, and are designed to be fast and clean. + +What operators and types does this support? +-- + +* Modifiers: `+` `-` `/` `*` `&` `|` `^` `**` `%` `>>` `<<` +* Comparators: `>` `>=` `<` `<=` `==` `!=` `=~` `!~` +* Logical ops: `||` `&&` +* Numeric constants, as 64-bit floating point (`12345.678`) +* String constants (single quotes: `'foobar'`) +* Date constants (single quotes, using any permutation of RFC3339, ISO8601, ruby date, or unix date; date parsing is automatically tried with any string constant) +* Boolean constants: `true` `false` +* Parenthesis to control order of evaluation `(` `)` +* Arrays (anything separated by `,` within parenthesis: `(1, 2, 'foo')`) +* Prefixes: `!` `-` `~` +* Ternary conditional: `?` `:` +* Null coalescence: `??` + +See [MANUAL.md](https://github.com/Knetic/govaluate/blob/master/MANUAL.md) for exacting details on what types each operator supports. + +Types +-- + +Some operators don't make sense when used with some types. For instance, what does it mean to get the modulo of a string? What happens if you check to see if two numbers are logically AND'ed together? + +Everyone has a different intuition about the answers to these questions. To prevent confusion, this library will _refuse to operate_ upon types for which there is not an unambiguous meaning for the operation. See [MANUAL.md](https://github.com/Knetic/govaluate/blob/master/MANUAL.md) for details about what operators are valid for which types. + +Benchmarks +-- + +If you're concerned about the overhead of this library, a good range of benchmarks are built into this repo. You can run them with `go test -bench=.`. The library is built with an eye towards being quick, but has not been aggressively profiled and optimized. For most applications, though, it is completely fine. + +For a very rough idea of performance, here are the results output from a benchmark run on a 3rd-gen Macbook Pro (Linux Mint 17.1). + +``` +BenchmarkSingleParse-12 1000000 1382 ns/op +BenchmarkSimpleParse-12 200000 10771 ns/op +BenchmarkFullParse-12 30000 49383 ns/op +BenchmarkEvaluationSingle-12 50000000 30.1 ns/op +BenchmarkEvaluationNumericLiteral-12 10000000 119 ns/op +BenchmarkEvaluationLiteralModifiers-12 10000000 236 ns/op +BenchmarkEvaluationParameters-12 5000000 260 ns/op +BenchmarkEvaluationParametersModifiers-12 3000000 547 ns/op +BenchmarkComplexExpression-12 2000000 963 ns/op +BenchmarkRegexExpression-12 100000 20357 ns/op +BenchmarkConstantRegexExpression-12 1000000 1392 ns/op +ok +``` + +API Breaks +-- + +While this library has very few cases which will ever result in an API break, it can (and [has](https://github.com/Knetic/govaluate/releases/tag/v2.0.0)) happened. If you are using this in production, vendor the commit you've tested against, or use gopkg.in to redirect your import (e.g., `import "gopkg.in/Knetic/govaluate.v2"`). Master branch (while infrequent) _may_ at some point contain API breaking changes, and the author will have no way to communicate these to downstreams, other than creating a new major release. + +Releases will explicitly state when an API break happens, and if they do not specify an API break it should be safe to upgrade. + +License +-- + +This project is licensed under the MIT general use license. You're free to integrate, fork, and play with this code as you feel fit without consulting the author, as long as you provide proper credit to the author in your works. diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/TokenKind.go b/image-scanner/vendor/github.com/Knetic/govaluate/TokenKind.go new file mode 100644 index 000000000..7c9516d2d --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/TokenKind.go @@ -0,0 +1,75 @@ +package govaluate + +/* + Represents all valid types of tokens that a token can be. +*/ +type TokenKind int + +const ( + UNKNOWN TokenKind = iota + + PREFIX + NUMERIC + BOOLEAN + STRING + PATTERN + TIME + VARIABLE + FUNCTION + SEPARATOR + ACCESSOR + + COMPARATOR + LOGICALOP + MODIFIER + + CLAUSE + CLAUSE_CLOSE + + TERNARY +) + +/* + GetTokenKindString returns a string that describes the given TokenKind. + e.g., when passed the NUMERIC TokenKind, this returns the string "NUMERIC". +*/ +func (kind TokenKind) String() string { + + switch kind { + + case PREFIX: + return "PREFIX" + case NUMERIC: + return "NUMERIC" + case BOOLEAN: + return "BOOLEAN" + case STRING: + return "STRING" + case PATTERN: + return "PATTERN" + case TIME: + return "TIME" + case VARIABLE: + return "VARIABLE" + case FUNCTION: + return "FUNCTION" + case SEPARATOR: + return "SEPARATOR" + case COMPARATOR: + return "COMPARATOR" + case LOGICALOP: + return "LOGICALOP" + case MODIFIER: + return "MODIFIER" + case CLAUSE: + return "CLAUSE" + case CLAUSE_CLOSE: + return "CLAUSE_CLOSE" + case TERNARY: + return "TERNARY" + case ACCESSOR: + return "ACCESSOR" + } + + return "UNKNOWN" +} diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/evaluationStage.go b/image-scanner/vendor/github.com/Knetic/govaluate/evaluationStage.go new file mode 100644 index 000000000..11ea58724 --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/evaluationStage.go @@ -0,0 +1,516 @@ +package govaluate + +import ( + "errors" + "fmt" + "math" + "reflect" + "regexp" + "strings" +) + +const ( + logicalErrorFormat string = "Value '%v' cannot be used with the logical operator '%v', it is not a bool" + modifierErrorFormat string = "Value '%v' cannot be used with the modifier '%v', it is not a number" + comparatorErrorFormat string = "Value '%v' cannot be used with the comparator '%v', it is not a number" + ternaryErrorFormat string = "Value '%v' cannot be used with the ternary operator '%v', it is not a bool" + prefixErrorFormat string = "Value '%v' cannot be used with the prefix '%v'" +) + +type evaluationOperator func(left interface{}, right interface{}, parameters Parameters) (interface{}, error) +type stageTypeCheck func(value interface{}) bool +type stageCombinedTypeCheck func(left interface{}, right interface{}) bool + +type evaluationStage struct { + symbol OperatorSymbol + + leftStage, rightStage *evaluationStage + + // the operation that will be used to evaluate this stage (such as adding [left] to [right] and return the result) + operator evaluationOperator + + // ensures that both left and right values are appropriate for this stage. Returns an error if they aren't operable. + leftTypeCheck stageTypeCheck + rightTypeCheck stageTypeCheck + + // if specified, will override whatever is used in "leftTypeCheck" and "rightTypeCheck". + // primarily used for specific operators that don't care which side a given type is on, but still requires one side to be of a given type + // (like string concat) + typeCheck stageCombinedTypeCheck + + // regardless of which type check is used, this string format will be used as the error message for type errors + typeErrorFormat string +} + +var ( + _true = interface{}(true) + _false = interface{}(false) +) + +func (this *evaluationStage) swapWith(other *evaluationStage) { + + temp := *other + other.setToNonStage(*this) + this.setToNonStage(temp) +} + +func (this *evaluationStage) setToNonStage(other evaluationStage) { + + this.symbol = other.symbol + this.operator = other.operator + this.leftTypeCheck = other.leftTypeCheck + this.rightTypeCheck = other.rightTypeCheck + this.typeCheck = other.typeCheck + this.typeErrorFormat = other.typeErrorFormat +} + +func (this *evaluationStage) isShortCircuitable() bool { + + switch this.symbol { + case AND: + fallthrough + case OR: + fallthrough + case TERNARY_TRUE: + fallthrough + case TERNARY_FALSE: + fallthrough + case COALESCE: + return true + } + + return false +} + +func noopStageRight(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return right, nil +} + +func addStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + + // string concat if either are strings + if isString(left) || isString(right) { + return fmt.Sprintf("%v%v", left, right), nil + } + + return left.(float64) + right.(float64), nil +} +func subtractStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return left.(float64) - right.(float64), nil +} +func multiplyStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return left.(float64) * right.(float64), nil +} +func divideStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return left.(float64) / right.(float64), nil +} +func exponentStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return math.Pow(left.(float64), right.(float64)), nil +} +func modulusStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return math.Mod(left.(float64), right.(float64)), nil +} +func gteStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + if isString(left) && isString(right) { + return boolIface(left.(string) >= right.(string)), nil + } + return boolIface(left.(float64) >= right.(float64)), nil +} +func gtStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + if isString(left) && isString(right) { + return boolIface(left.(string) > right.(string)), nil + } + return boolIface(left.(float64) > right.(float64)), nil +} +func lteStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + if isString(left) && isString(right) { + return boolIface(left.(string) <= right.(string)), nil + } + return boolIface(left.(float64) <= right.(float64)), nil +} +func ltStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + if isString(left) && isString(right) { + return boolIface(left.(string) < right.(string)), nil + } + return boolIface(left.(float64) < right.(float64)), nil +} +func equalStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return boolIface(reflect.DeepEqual(left, right)), nil +} +func notEqualStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return boolIface(!reflect.DeepEqual(left, right)), nil +} +func andStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return boolIface(left.(bool) && right.(bool)), nil +} +func orStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return boolIface(left.(bool) || right.(bool)), nil +} +func negateStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return -right.(float64), nil +} +func invertStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return boolIface(!right.(bool)), nil +} +func bitwiseNotStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return float64(^int64(right.(float64))), nil +} +func ternaryIfStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + if left.(bool) { + return right, nil + } + return nil, nil +} +func ternaryElseStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + if left != nil { + return left, nil + } + return right, nil +} + +func regexStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + + var pattern *regexp.Regexp + var err error + + switch right.(type) { + case string: + pattern, err = regexp.Compile(right.(string)) + if err != nil { + return nil, errors.New(fmt.Sprintf("Unable to compile regexp pattern '%v': %v", right, err)) + } + case *regexp.Regexp: + pattern = right.(*regexp.Regexp) + } + + return pattern.Match([]byte(left.(string))), nil +} + +func notRegexStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + + ret, err := regexStage(left, right, parameters) + if err != nil { + return nil, err + } + + return !(ret.(bool)), nil +} + +func bitwiseOrStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return float64(int64(left.(float64)) | int64(right.(float64))), nil +} +func bitwiseAndStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return float64(int64(left.(float64)) & int64(right.(float64))), nil +} +func bitwiseXORStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return float64(int64(left.(float64)) ^ int64(right.(float64))), nil +} +func leftShiftStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return float64(uint64(left.(float64)) << uint64(right.(float64))), nil +} +func rightShiftStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return float64(uint64(left.(float64)) >> uint64(right.(float64))), nil +} + +func makeParameterStage(parameterName string) evaluationOperator { + + return func(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + value, err := parameters.Get(parameterName) + if err != nil { + return nil, err + } + + return value, nil + } +} + +func makeLiteralStage(literal interface{}) evaluationOperator { + return func(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return literal, nil + } +} + +func makeFunctionStage(function ExpressionFunction) evaluationOperator { + + return func(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + + if right == nil { + return function() + } + + switch right.(type) { + case []interface{}: + return function(right.([]interface{})...) + default: + return function(right) + } + } +} + +func typeConvertParam(p reflect.Value, t reflect.Type) (ret reflect.Value, err error) { + defer func() { + if r := recover(); r != nil { + errorMsg := fmt.Sprintf("Argument type conversion failed: failed to convert '%s' to '%s'", p.Kind().String(), t.Kind().String()) + err = errors.New(errorMsg) + ret = p + } + }() + + return p.Convert(t), nil +} + +func typeConvertParams(method reflect.Value, params []reflect.Value) ([]reflect.Value, error) { + + methodType := method.Type() + numIn := methodType.NumIn() + numParams := len(params) + + if numIn != numParams { + if numIn > numParams { + return nil, fmt.Errorf("Too few arguments to parameter call: got %d arguments, expected %d", len(params), numIn) + } + return nil, fmt.Errorf("Too many arguments to parameter call: got %d arguments, expected %d", len(params), numIn) + } + + for i := 0; i < numIn; i++ { + t := methodType.In(i) + p := params[i] + pt := p.Type() + + if t.Kind() != pt.Kind() { + np, err := typeConvertParam(p, t) + if err != nil { + return nil, err + } + params[i] = np + } + } + + return params, nil +} + +func makeAccessorStage(pair []string) evaluationOperator { + + reconstructed := strings.Join(pair, ".") + + return func(left interface{}, right interface{}, parameters Parameters) (ret interface{}, err error) { + + var params []reflect.Value + + value, err := parameters.Get(pair[0]) + if err != nil { + return nil, err + } + + // while this library generally tries to handle panic-inducing cases on its own, + // accessors are a sticky case which have a lot of possible ways to fail. + // therefore every call to an accessor sets up a defer that tries to recover from panics, converting them to errors. + defer func() { + if r := recover(); r != nil { + errorMsg := fmt.Sprintf("Failed to access '%s': %v", reconstructed, r.(string)) + err = errors.New(errorMsg) + ret = nil + } + }() + + for i := 1; i < len(pair); i++ { + + coreValue := reflect.ValueOf(value) + + var corePtrVal reflect.Value + + // if this is a pointer, resolve it. + if coreValue.Kind() == reflect.Ptr { + corePtrVal = coreValue + coreValue = coreValue.Elem() + } + + if coreValue.Kind() != reflect.Struct { + return nil, errors.New("Unable to access '" + pair[i] + "', '" + pair[i-1] + "' is not a struct") + } + + field := coreValue.FieldByName(pair[i]) + if field != (reflect.Value{}) { + value = field.Interface() + continue + } + + method := coreValue.MethodByName(pair[i]) + if method == (reflect.Value{}) { + if corePtrVal.IsValid() { + method = corePtrVal.MethodByName(pair[i]) + } + if method == (reflect.Value{}) { + return nil, errors.New("No method or field '" + pair[i] + "' present on parameter '" + pair[i-1] + "'") + } + } + + switch right.(type) { + case []interface{}: + + givenParams := right.([]interface{}) + params = make([]reflect.Value, len(givenParams)) + for idx, _ := range givenParams { + params[idx] = reflect.ValueOf(givenParams[idx]) + } + + default: + + if right == nil { + params = []reflect.Value{} + break + } + + params = []reflect.Value{reflect.ValueOf(right.(interface{}))} + } + + params, err = typeConvertParams(method, params) + + if err != nil { + return nil, errors.New("Method call failed - '" + pair[0] + "." + pair[1] + "': " + err.Error()) + } + + returned := method.Call(params) + retLength := len(returned) + + if retLength == 0 { + return nil, errors.New("Method call '" + pair[i-1] + "." + pair[i] + "' did not return any values.") + } + + if retLength == 1 { + + value = returned[0].Interface() + continue + } + + if retLength == 2 { + + errIface := returned[1].Interface() + err, validType := errIface.(error) + + if validType && errIface != nil { + return returned[0].Interface(), err + } + + value = returned[0].Interface() + continue + } + + return nil, errors.New("Method call '" + pair[0] + "." + pair[1] + "' did not return either one value, or a value and an error. Cannot interpret meaning.") + } + + value = castToFloat64(value) + return value, nil + } +} + +func separatorStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + + var ret []interface{} + + switch left.(type) { + case []interface{}: + ret = append(left.([]interface{}), right) + default: + ret = []interface{}{left, right} + } + + return ret, nil +} + +func inStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + + for _, value := range right.([]interface{}) { + if left == value { + return true, nil + } + } + return false, nil +} + +// + +func isString(value interface{}) bool { + + switch value.(type) { + case string: + return true + } + return false +} + +func isRegexOrString(value interface{}) bool { + + switch value.(type) { + case string: + return true + case *regexp.Regexp: + return true + } + return false +} + +func isBool(value interface{}) bool { + switch value.(type) { + case bool: + return true + } + return false +} + +func isFloat64(value interface{}) bool { + switch value.(type) { + case float64: + return true + } + return false +} + +/* + Addition usually means between numbers, but can also mean string concat. + String concat needs one (or both) of the sides to be a string. +*/ +func additionTypeCheck(left interface{}, right interface{}) bool { + + if isFloat64(left) && isFloat64(right) { + return true + } + if !isString(left) && !isString(right) { + return false + } + return true +} + +/* + Comparison can either be between numbers, or lexicographic between two strings, + but never between the two. +*/ +func comparatorTypeCheck(left interface{}, right interface{}) bool { + + if isFloat64(left) && isFloat64(right) { + return true + } + if isString(left) && isString(right) { + return true + } + return false +} + +func isArray(value interface{}) bool { + switch value.(type) { + case []interface{}: + return true + } + return false +} + +/* + Converting a boolean to an interface{} requires an allocation. + We can use interned bools to avoid this cost. +*/ +func boolIface(b bool) interface{} { + if b { + return _true + } + return _false +} diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/expressionFunctions.go b/image-scanner/vendor/github.com/Knetic/govaluate/expressionFunctions.go new file mode 100644 index 000000000..ac6592b3f --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/expressionFunctions.go @@ -0,0 +1,8 @@ +package govaluate + +/* + Represents a function that can be called from within an expression. + This method must return an error if, for any reason, it is unable to produce exactly one unambiguous result. + An error returned will halt execution of the expression. +*/ +type ExpressionFunction func(arguments ...interface{}) (interface{}, error) diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/expressionOutputStream.go b/image-scanner/vendor/github.com/Knetic/govaluate/expressionOutputStream.go new file mode 100644 index 000000000..88a841639 --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/expressionOutputStream.go @@ -0,0 +1,46 @@ +package govaluate + +import ( + "bytes" +) + +/* + Holds a series of "transactions" which represent each token as it is output by an outputter (such as ToSQLQuery()). + Some outputs (such as SQL) require a function call or non-c-like syntax to represent an expression. + To accomplish this, this struct keeps track of each translated token as it is output, and can return and rollback those transactions. +*/ +type expressionOutputStream struct { + transactions []string +} + +func (this *expressionOutputStream) add(transaction string) { + this.transactions = append(this.transactions, transaction) +} + +func (this *expressionOutputStream) rollback() string { + + index := len(this.transactions) - 1 + ret := this.transactions[index] + + this.transactions = this.transactions[:index] + return ret +} + +func (this *expressionOutputStream) createString(delimiter string) string { + + var retBuffer bytes.Buffer + var transaction string + + penultimate := len(this.transactions) - 1 + + for i := 0; i < penultimate; i++ { + + transaction = this.transactions[i] + + retBuffer.WriteString(transaction) + retBuffer.WriteString(delimiter) + } + retBuffer.WriteString(this.transactions[penultimate]) + + return retBuffer.String() +} diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/lexerState.go b/image-scanner/vendor/github.com/Knetic/govaluate/lexerState.go new file mode 100644 index 000000000..6726e909e --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/lexerState.go @@ -0,0 +1,373 @@ +package govaluate + +import ( + "errors" + "fmt" +) + +type lexerState struct { + isEOF bool + isNullable bool + kind TokenKind + validNextKinds []TokenKind +} + +// lexer states. +// Constant for all purposes except compiler. +var validLexerStates = []lexerState{ + + lexerState{ + kind: UNKNOWN, + isEOF: false, + isNullable: true, + validNextKinds: []TokenKind{ + + PREFIX, + NUMERIC, + BOOLEAN, + VARIABLE, + PATTERN, + FUNCTION, + ACCESSOR, + STRING, + TIME, + CLAUSE, + }, + }, + + lexerState{ + + kind: CLAUSE, + isEOF: false, + isNullable: true, + validNextKinds: []TokenKind{ + + PREFIX, + NUMERIC, + BOOLEAN, + VARIABLE, + PATTERN, + FUNCTION, + ACCESSOR, + STRING, + TIME, + CLAUSE, + CLAUSE_CLOSE, + }, + }, + + lexerState{ + + kind: CLAUSE_CLOSE, + isEOF: true, + isNullable: true, + validNextKinds: []TokenKind{ + + COMPARATOR, + MODIFIER, + NUMERIC, + BOOLEAN, + VARIABLE, + STRING, + PATTERN, + TIME, + CLAUSE, + CLAUSE_CLOSE, + LOGICALOP, + TERNARY, + SEPARATOR, + }, + }, + + lexerState{ + + kind: NUMERIC, + isEOF: true, + isNullable: false, + validNextKinds: []TokenKind{ + + MODIFIER, + COMPARATOR, + LOGICALOP, + CLAUSE_CLOSE, + TERNARY, + SEPARATOR, + }, + }, + lexerState{ + + kind: BOOLEAN, + isEOF: true, + isNullable: false, + validNextKinds: []TokenKind{ + + MODIFIER, + COMPARATOR, + LOGICALOP, + CLAUSE_CLOSE, + TERNARY, + SEPARATOR, + }, + }, + lexerState{ + + kind: STRING, + isEOF: true, + isNullable: false, + validNextKinds: []TokenKind{ + + MODIFIER, + COMPARATOR, + LOGICALOP, + CLAUSE_CLOSE, + TERNARY, + SEPARATOR, + }, + }, + lexerState{ + + kind: TIME, + isEOF: true, + isNullable: false, + validNextKinds: []TokenKind{ + + MODIFIER, + COMPARATOR, + LOGICALOP, + CLAUSE_CLOSE, + SEPARATOR, + }, + }, + lexerState{ + + kind: PATTERN, + isEOF: true, + isNullable: false, + validNextKinds: []TokenKind{ + + MODIFIER, + COMPARATOR, + LOGICALOP, + CLAUSE_CLOSE, + SEPARATOR, + }, + }, + lexerState{ + + kind: VARIABLE, + isEOF: true, + isNullable: false, + validNextKinds: []TokenKind{ + + MODIFIER, + COMPARATOR, + LOGICALOP, + CLAUSE_CLOSE, + TERNARY, + SEPARATOR, + }, + }, + lexerState{ + + kind: MODIFIER, + isEOF: false, + isNullable: false, + validNextKinds: []TokenKind{ + + PREFIX, + NUMERIC, + VARIABLE, + FUNCTION, + ACCESSOR, + STRING, + BOOLEAN, + CLAUSE, + CLAUSE_CLOSE, + }, + }, + lexerState{ + + kind: COMPARATOR, + isEOF: false, + isNullable: false, + validNextKinds: []TokenKind{ + + PREFIX, + NUMERIC, + BOOLEAN, + VARIABLE, + FUNCTION, + ACCESSOR, + STRING, + TIME, + CLAUSE, + CLAUSE_CLOSE, + PATTERN, + }, + }, + lexerState{ + + kind: LOGICALOP, + isEOF: false, + isNullable: false, + validNextKinds: []TokenKind{ + + PREFIX, + NUMERIC, + BOOLEAN, + VARIABLE, + FUNCTION, + ACCESSOR, + STRING, + TIME, + CLAUSE, + CLAUSE_CLOSE, + }, + }, + lexerState{ + + kind: PREFIX, + isEOF: false, + isNullable: false, + validNextKinds: []TokenKind{ + + NUMERIC, + BOOLEAN, + VARIABLE, + FUNCTION, + ACCESSOR, + CLAUSE, + CLAUSE_CLOSE, + }, + }, + + lexerState{ + + kind: TERNARY, + isEOF: false, + isNullable: false, + validNextKinds: []TokenKind{ + + PREFIX, + NUMERIC, + BOOLEAN, + STRING, + TIME, + VARIABLE, + FUNCTION, + ACCESSOR, + CLAUSE, + SEPARATOR, + }, + }, + lexerState{ + + kind: FUNCTION, + isEOF: false, + isNullable: false, + validNextKinds: []TokenKind{ + CLAUSE, + }, + }, + lexerState{ + + kind: ACCESSOR, + isEOF: true, + isNullable: false, + validNextKinds: []TokenKind{ + CLAUSE, + MODIFIER, + COMPARATOR, + LOGICALOP, + CLAUSE_CLOSE, + TERNARY, + SEPARATOR, + }, + }, + lexerState{ + + kind: SEPARATOR, + isEOF: false, + isNullable: true, + validNextKinds: []TokenKind{ + + PREFIX, + NUMERIC, + BOOLEAN, + STRING, + TIME, + VARIABLE, + FUNCTION, + ACCESSOR, + CLAUSE, + }, + }, +} + +func (this lexerState) canTransitionTo(kind TokenKind) bool { + + for _, validKind := range this.validNextKinds { + + if validKind == kind { + return true + } + } + + return false +} + +func checkExpressionSyntax(tokens []ExpressionToken) error { + + var state lexerState + var lastToken ExpressionToken + var err error + + state = validLexerStates[0] + + for _, token := range tokens { + + if !state.canTransitionTo(token.Kind) { + + // call out a specific error for tokens looking like they want to be functions. + if lastToken.Kind == VARIABLE && token.Kind == CLAUSE { + return errors.New("Undefined function " + lastToken.Value.(string)) + } + + firstStateName := fmt.Sprintf("%s [%v]", state.kind.String(), lastToken.Value) + nextStateName := fmt.Sprintf("%s [%v]", token.Kind.String(), token.Value) + + return errors.New("Cannot transition token types from " + firstStateName + " to " + nextStateName) + } + + state, err = getLexerStateForToken(token.Kind) + if err != nil { + return err + } + + if !state.isNullable && token.Value == nil { + + errorMsg := fmt.Sprintf("Token kind '%v' cannot have a nil value", token.Kind.String()) + return errors.New(errorMsg) + } + + lastToken = token + } + + if !state.isEOF { + return errors.New("Unexpected end of expression") + } + return nil +} + +func getLexerStateForToken(kind TokenKind) (lexerState, error) { + + for _, possibleState := range validLexerStates { + + if possibleState.kind == kind { + return possibleState, nil + } + } + + errorMsg := fmt.Sprintf("No lexer state found for token kind '%v'\n", kind.String()) + return validLexerStates[0], errors.New(errorMsg) +} diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/lexerStream.go b/image-scanner/vendor/github.com/Knetic/govaluate/lexerStream.go new file mode 100644 index 000000000..b72e6bdb1 --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/lexerStream.go @@ -0,0 +1,39 @@ +package govaluate + +type lexerStream struct { + source []rune + position int + length int +} + +func newLexerStream(source string) *lexerStream { + + var ret *lexerStream + var runes []rune + + for _, character := range source { + runes = append(runes, character) + } + + ret = new(lexerStream) + ret.source = runes + ret.length = len(runes) + return ret +} + +func (this *lexerStream) readCharacter() rune { + + var character rune + + character = this.source[this.position] + this.position += 1 + return character +} + +func (this *lexerStream) rewind(amount int) { + this.position -= amount +} + +func (this lexerStream) canRead() bool { + return this.position < this.length +} diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/parameters.go b/image-scanner/vendor/github.com/Knetic/govaluate/parameters.go new file mode 100644 index 000000000..6c5b9ecb5 --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/parameters.go @@ -0,0 +1,32 @@ +package govaluate + +import ( + "errors" +) + +/* + Parameters is a collection of named parameters that can be used by an EvaluableExpression to retrieve parameters + when an expression tries to use them. +*/ +type Parameters interface { + + /* + Get gets the parameter of the given name, or an error if the parameter is unavailable. + Failure to find the given parameter should be indicated by returning an error. + */ + Get(name string) (interface{}, error) +} + +type MapParameters map[string]interface{} + +func (p MapParameters) Get(name string) (interface{}, error) { + + value, found := p[name] + + if !found { + errorMessage := "No parameter '" + name + "' found." + return nil, errors.New(errorMessage) + } + + return value, nil +} diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/parsing.go b/image-scanner/vendor/github.com/Knetic/govaluate/parsing.go new file mode 100644 index 000000000..40c7ed2c4 --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/parsing.go @@ -0,0 +1,526 @@ +package govaluate + +import ( + "bytes" + "errors" + "fmt" + "regexp" + "strconv" + "strings" + "time" + "unicode" +) + +func parseTokens(expression string, functions map[string]ExpressionFunction) ([]ExpressionToken, error) { + + var ret []ExpressionToken + var token ExpressionToken + var stream *lexerStream + var state lexerState + var err error + var found bool + + stream = newLexerStream(expression) + state = validLexerStates[0] + + for stream.canRead() { + + token, err, found = readToken(stream, state, functions) + + if err != nil { + return ret, err + } + + if !found { + break + } + + state, err = getLexerStateForToken(token.Kind) + if err != nil { + return ret, err + } + + // append this valid token + ret = append(ret, token) + } + + err = checkBalance(ret) + if err != nil { + return nil, err + } + + return ret, nil +} + +func readToken(stream *lexerStream, state lexerState, functions map[string]ExpressionFunction) (ExpressionToken, error, bool) { + + var function ExpressionFunction + var ret ExpressionToken + var tokenValue interface{} + var tokenTime time.Time + var tokenString string + var kind TokenKind + var character rune + var found bool + var completed bool + var err error + + // numeric is 0-9, or . or 0x followed by digits + // string starts with ' + // variable is alphanumeric, always starts with a letter + // bracket always means variable + // symbols are anything non-alphanumeric + // all others read into a buffer until they reach the end of the stream + for stream.canRead() { + + character = stream.readCharacter() + + if unicode.IsSpace(character) { + continue + } + + kind = UNKNOWN + + // numeric constant + if isNumeric(character) { + + if stream.canRead() && character == '0' { + character = stream.readCharacter() + + if stream.canRead() && character == 'x' { + tokenString, _ = readUntilFalse(stream, false, true, true, isHexDigit) + tokenValueInt, err := strconv.ParseUint(tokenString, 16, 64) + + if err != nil { + errorMsg := fmt.Sprintf("Unable to parse hex value '%v' to uint64\n", tokenString) + return ExpressionToken{}, errors.New(errorMsg), false + } + + kind = NUMERIC + tokenValue = float64(tokenValueInt) + break + } else { + stream.rewind(1) + } + } + + tokenString = readTokenUntilFalse(stream, isNumeric) + tokenValue, err = strconv.ParseFloat(tokenString, 64) + + if err != nil { + errorMsg := fmt.Sprintf("Unable to parse numeric value '%v' to float64\n", tokenString) + return ExpressionToken{}, errors.New(errorMsg), false + } + kind = NUMERIC + break + } + + // comma, separator + if character == ',' { + + tokenValue = "," + kind = SEPARATOR + break + } + + // escaped variable + if character == '[' { + + tokenValue, completed = readUntilFalse(stream, true, false, true, isNotClosingBracket) + kind = VARIABLE + + if !completed { + return ExpressionToken{}, errors.New("Unclosed parameter bracket"), false + } + + // above method normally rewinds us to the closing bracket, which we want to skip. + stream.rewind(-1) + break + } + + // regular variable - or function? + if unicode.IsLetter(character) { + + tokenString = readTokenUntilFalse(stream, isVariableName) + + tokenValue = tokenString + kind = VARIABLE + + // boolean? + if tokenValue == "true" { + + kind = BOOLEAN + tokenValue = true + } else { + + if tokenValue == "false" { + + kind = BOOLEAN + tokenValue = false + } + } + + // textual operator? + if tokenValue == "in" || tokenValue == "IN" { + + // force lower case for consistency + tokenValue = "in" + kind = COMPARATOR + } + + // function? + function, found = functions[tokenString] + if found { + kind = FUNCTION + tokenValue = function + } + + // accessor? + accessorIndex := strings.Index(tokenString, ".") + if accessorIndex > 0 { + + // check that it doesn't end with a hanging period + if tokenString[len(tokenString)-1] == '.' { + errorMsg := fmt.Sprintf("Hanging accessor on token '%s'", tokenString) + return ExpressionToken{}, errors.New(errorMsg), false + } + + kind = ACCESSOR + splits := strings.Split(tokenString, ".") + tokenValue = splits + + // check that none of them are unexported + for i := 1; i < len(splits); i++ { + + firstCharacter := getFirstRune(splits[i]) + + if unicode.ToUpper(firstCharacter) != firstCharacter { + errorMsg := fmt.Sprintf("Unable to access unexported field '%s' in token '%s'", splits[i], tokenString) + return ExpressionToken{}, errors.New(errorMsg), false + } + } + } + break + } + + if !isNotQuote(character) { + tokenValue, completed = readUntilFalse(stream, true, false, true, isNotQuote) + + if !completed { + return ExpressionToken{}, errors.New("Unclosed string literal"), false + } + + // advance the stream one position, since reading until false assumes the terminator is a real token + stream.rewind(-1) + + // check to see if this can be parsed as a time. + tokenTime, found = tryParseTime(tokenValue.(string)) + if found { + kind = TIME + tokenValue = tokenTime + } else { + kind = STRING + } + break + } + + if character == '(' { + tokenValue = character + kind = CLAUSE + break + } + + if character == ')' { + tokenValue = character + kind = CLAUSE_CLOSE + break + } + + // must be a known symbol + tokenString = readTokenUntilFalse(stream, isNotAlphanumeric) + tokenValue = tokenString + + // quick hack for the case where "-" can mean "prefixed negation" or "minus", which are used + // very differently. + if state.canTransitionTo(PREFIX) { + _, found = prefixSymbols[tokenString] + if found { + + kind = PREFIX + break + } + } + _, found = modifierSymbols[tokenString] + if found { + + kind = MODIFIER + break + } + + _, found = logicalSymbols[tokenString] + if found { + + kind = LOGICALOP + break + } + + _, found = comparatorSymbols[tokenString] + if found { + + kind = COMPARATOR + break + } + + _, found = ternarySymbols[tokenString] + if found { + + kind = TERNARY + break + } + + errorMessage := fmt.Sprintf("Invalid token: '%s'", tokenString) + return ret, errors.New(errorMessage), false + } + + ret.Kind = kind + ret.Value = tokenValue + + return ret, nil, (kind != UNKNOWN) +} + +func readTokenUntilFalse(stream *lexerStream, condition func(rune) bool) string { + + var ret string + + stream.rewind(1) + ret, _ = readUntilFalse(stream, false, true, true, condition) + return ret +} + +/* + Returns the string that was read until the given [condition] was false, or whitespace was broken. + Returns false if the stream ended before whitespace was broken or condition was met. +*/ +func readUntilFalse(stream *lexerStream, includeWhitespace bool, breakWhitespace bool, allowEscaping bool, condition func(rune) bool) (string, bool) { + + var tokenBuffer bytes.Buffer + var character rune + var conditioned bool + + conditioned = false + + for stream.canRead() { + + character = stream.readCharacter() + + // Use backslashes to escape anything + if allowEscaping && character == '\\' { + + character = stream.readCharacter() + tokenBuffer.WriteString(string(character)) + continue + } + + if unicode.IsSpace(character) { + + if breakWhitespace && tokenBuffer.Len() > 0 { + conditioned = true + break + } + if !includeWhitespace { + continue + } + } + + if condition(character) { + tokenBuffer.WriteString(string(character)) + } else { + conditioned = true + stream.rewind(1) + break + } + } + + return tokenBuffer.String(), conditioned +} + +/* + Checks to see if any optimizations can be performed on the given [tokens], which form a complete, valid expression. + The returns slice will represent the optimized (or unmodified) list of tokens to use. +*/ +func optimizeTokens(tokens []ExpressionToken) ([]ExpressionToken, error) { + + var token ExpressionToken + var symbol OperatorSymbol + var err error + var index int + + for index, token = range tokens { + + // if we find a regex operator, and the right-hand value is a constant, precompile and replace with a pattern. + if token.Kind != COMPARATOR { + continue + } + + symbol = comparatorSymbols[token.Value.(string)] + if symbol != REQ && symbol != NREQ { + continue + } + + index++ + token = tokens[index] + if token.Kind == STRING { + + token.Kind = PATTERN + token.Value, err = regexp.Compile(token.Value.(string)) + + if err != nil { + return tokens, err + } + + tokens[index] = token + } + } + return tokens, nil +} + +/* + Checks the balance of tokens which have multiple parts, such as parenthesis. +*/ +func checkBalance(tokens []ExpressionToken) error { + + var stream *tokenStream + var token ExpressionToken + var parens int + + stream = newTokenStream(tokens) + + for stream.hasNext() { + + token = stream.next() + if token.Kind == CLAUSE { + parens++ + continue + } + if token.Kind == CLAUSE_CLOSE { + parens-- + continue + } + } + + if parens != 0 { + return errors.New("Unbalanced parenthesis") + } + return nil +} + +func isDigit(character rune) bool { + return unicode.IsDigit(character) +} + +func isHexDigit(character rune) bool { + + character = unicode.ToLower(character) + + return unicode.IsDigit(character) || + character == 'a' || + character == 'b' || + character == 'c' || + character == 'd' || + character == 'e' || + character == 'f' +} + +func isNumeric(character rune) bool { + + return unicode.IsDigit(character) || character == '.' +} + +func isNotQuote(character rune) bool { + + return character != '\'' && character != '"' +} + +func isNotAlphanumeric(character rune) bool { + + return !(unicode.IsDigit(character) || + unicode.IsLetter(character) || + character == '(' || + character == ')' || + character == '[' || + character == ']' || // starting to feel like there needs to be an `isOperation` func (#59) + !isNotQuote(character)) +} + +func isVariableName(character rune) bool { + + return unicode.IsLetter(character) || + unicode.IsDigit(character) || + character == '_' || + character == '.' +} + +func isNotClosingBracket(character rune) bool { + + return character != ']' +} + +/* + Attempts to parse the [candidate] as a Time. + Tries a series of standardized date formats, returns the Time if one applies, + otherwise returns false through the second return. +*/ +func tryParseTime(candidate string) (time.Time, bool) { + + var ret time.Time + var found bool + + timeFormats := [...]string{ + time.ANSIC, + time.UnixDate, + time.RubyDate, + time.Kitchen, + time.RFC3339, + time.RFC3339Nano, + "2006-01-02", // RFC 3339 + "2006-01-02 15:04", // RFC 3339 with minutes + "2006-01-02 15:04:05", // RFC 3339 with seconds + "2006-01-02 15:04:05-07:00", // RFC 3339 with seconds and timezone + "2006-01-02T15Z0700", // ISO8601 with hour + "2006-01-02T15:04Z0700", // ISO8601 with minutes + "2006-01-02T15:04:05Z0700", // ISO8601 with seconds + "2006-01-02T15:04:05.999999999Z0700", // ISO8601 with nanoseconds + } + + for _, format := range timeFormats { + + ret, found = tryParseExactTime(candidate, format) + if found { + return ret, true + } + } + + return time.Now(), false +} + +func tryParseExactTime(candidate string, format string) (time.Time, bool) { + + var ret time.Time + var err error + + ret, err = time.ParseInLocation(format, candidate, time.Local) + if err != nil { + return time.Now(), false + } + + return ret, true +} + +func getFirstRune(candidate string) rune { + + for _, character := range candidate { + return character + } + + return 0 +} diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/sanitizedParameters.go b/image-scanner/vendor/github.com/Knetic/govaluate/sanitizedParameters.go new file mode 100644 index 000000000..28bd795d9 --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/sanitizedParameters.go @@ -0,0 +1,43 @@ +package govaluate + +// sanitizedParameters is a wrapper for Parameters that does sanitization as +// parameters are accessed. +type sanitizedParameters struct { + orig Parameters +} + +func (p sanitizedParameters) Get(key string) (interface{}, error) { + value, err := p.orig.Get(key) + if err != nil { + return nil, err + } + + return castToFloat64(value), nil +} + +func castToFloat64(value interface{}) interface{} { + switch value.(type) { + case uint8: + return float64(value.(uint8)) + case uint16: + return float64(value.(uint16)) + case uint32: + return float64(value.(uint32)) + case uint64: + return float64(value.(uint64)) + case int8: + return float64(value.(int8)) + case int16: + return float64(value.(int16)) + case int32: + return float64(value.(int32)) + case int64: + return float64(value.(int64)) + case int: + return float64(value.(int)) + case float32: + return float64(value.(float32)) + } + + return value +} diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/stagePlanner.go b/image-scanner/vendor/github.com/Knetic/govaluate/stagePlanner.go new file mode 100644 index 000000000..d71ed129d --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/stagePlanner.go @@ -0,0 +1,724 @@ +package govaluate + +import ( + "errors" + "fmt" + "time" +) + +var stageSymbolMap = map[OperatorSymbol]evaluationOperator{ + EQ: equalStage, + NEQ: notEqualStage, + GT: gtStage, + LT: ltStage, + GTE: gteStage, + LTE: lteStage, + REQ: regexStage, + NREQ: notRegexStage, + AND: andStage, + OR: orStage, + IN: inStage, + BITWISE_OR: bitwiseOrStage, + BITWISE_AND: bitwiseAndStage, + BITWISE_XOR: bitwiseXORStage, + BITWISE_LSHIFT: leftShiftStage, + BITWISE_RSHIFT: rightShiftStage, + PLUS: addStage, + MINUS: subtractStage, + MULTIPLY: multiplyStage, + DIVIDE: divideStage, + MODULUS: modulusStage, + EXPONENT: exponentStage, + NEGATE: negateStage, + INVERT: invertStage, + BITWISE_NOT: bitwiseNotStage, + TERNARY_TRUE: ternaryIfStage, + TERNARY_FALSE: ternaryElseStage, + COALESCE: ternaryElseStage, + SEPARATE: separatorStage, +} + +/* + A "precedent" is a function which will recursively parse new evaluateionStages from a given stream of tokens. + It's called a `precedent` because it is expected to handle exactly what precedence of operator, + and defer to other `precedent`s for other operators. +*/ +type precedent func(stream *tokenStream) (*evaluationStage, error) + +/* + A convenience function for specifying the behavior of a `precedent`. + Most `precedent` functions can be described by the same function, just with different type checks, symbols, and error formats. + This struct is passed to `makePrecedentFromPlanner` to create a `precedent` function. +*/ +type precedencePlanner struct { + validSymbols map[string]OperatorSymbol + validKinds []TokenKind + + typeErrorFormat string + + next precedent + nextRight precedent +} + +var planPrefix precedent +var planExponential precedent +var planMultiplicative precedent +var planAdditive precedent +var planBitwise precedent +var planShift precedent +var planComparator precedent +var planLogicalAnd precedent +var planLogicalOr precedent +var planTernary precedent +var planSeparator precedent + +func init() { + + // all these stages can use the same code (in `planPrecedenceLevel`) to execute, + // they simply need different type checks, symbols, and recursive precedents. + // While not all precedent phases are listed here, most can be represented this way. + planPrefix = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: prefixSymbols, + validKinds: []TokenKind{PREFIX}, + typeErrorFormat: prefixErrorFormat, + nextRight: planFunction, + }) + planExponential = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: exponentialSymbolsS, + validKinds: []TokenKind{MODIFIER}, + typeErrorFormat: modifierErrorFormat, + next: planFunction, + }) + planMultiplicative = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: multiplicativeSymbols, + validKinds: []TokenKind{MODIFIER}, + typeErrorFormat: modifierErrorFormat, + next: planExponential, + }) + planAdditive = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: additiveSymbols, + validKinds: []TokenKind{MODIFIER}, + typeErrorFormat: modifierErrorFormat, + next: planMultiplicative, + }) + planShift = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: bitwiseShiftSymbols, + validKinds: []TokenKind{MODIFIER}, + typeErrorFormat: modifierErrorFormat, + next: planAdditive, + }) + planBitwise = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: bitwiseSymbols, + validKinds: []TokenKind{MODIFIER}, + typeErrorFormat: modifierErrorFormat, + next: planShift, + }) + planComparator = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: comparatorSymbols, + validKinds: []TokenKind{COMPARATOR}, + typeErrorFormat: comparatorErrorFormat, + next: planBitwise, + }) + planLogicalAnd = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: map[string]OperatorSymbol{"&&": AND}, + validKinds: []TokenKind{LOGICALOP}, + typeErrorFormat: logicalErrorFormat, + next: planComparator, + }) + planLogicalOr = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: map[string]OperatorSymbol{"||": OR}, + validKinds: []TokenKind{LOGICALOP}, + typeErrorFormat: logicalErrorFormat, + next: planLogicalAnd, + }) + planTernary = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: ternarySymbols, + validKinds: []TokenKind{TERNARY}, + typeErrorFormat: ternaryErrorFormat, + next: planLogicalOr, + }) + planSeparator = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: separatorSymbols, + validKinds: []TokenKind{SEPARATOR}, + next: planTernary, + }) +} + +/* + Given a planner, creates a function which will evaluate a specific precedence level of operators, + and link it to other `precedent`s which recurse to parse other precedence levels. +*/ +func makePrecedentFromPlanner(planner *precedencePlanner) precedent { + + var generated precedent + var nextRight precedent + + generated = func(stream *tokenStream) (*evaluationStage, error) { + return planPrecedenceLevel( + stream, + planner.typeErrorFormat, + planner.validSymbols, + planner.validKinds, + nextRight, + planner.next, + ) + } + + if planner.nextRight != nil { + nextRight = planner.nextRight + } else { + nextRight = generated + } + + return generated +} + +/* + Creates a `evaluationStageList` object which represents an execution plan (or tree) + which is used to completely evaluate a set of tokens at evaluation-time. + The three stages of evaluation can be thought of as parsing strings to tokens, then tokens to a stage list, then evaluation with parameters. +*/ +func planStages(tokens []ExpressionToken) (*evaluationStage, error) { + + stream := newTokenStream(tokens) + + stage, err := planTokens(stream) + if err != nil { + return nil, err + } + + // while we're now fully-planned, we now need to re-order same-precedence operators. + // this could probably be avoided with a different planning method + reorderStages(stage) + + stage = elideLiterals(stage) + return stage, nil +} + +func planTokens(stream *tokenStream) (*evaluationStage, error) { + + if !stream.hasNext() { + return nil, nil + } + + return planSeparator(stream) +} + +/* + The most usual method of parsing an evaluation stage for a given precedence. + Most stages use the same logic +*/ +func planPrecedenceLevel( + stream *tokenStream, + typeErrorFormat string, + validSymbols map[string]OperatorSymbol, + validKinds []TokenKind, + rightPrecedent precedent, + leftPrecedent precedent) (*evaluationStage, error) { + + var token ExpressionToken + var symbol OperatorSymbol + var leftStage, rightStage *evaluationStage + var checks typeChecks + var err error + var keyFound bool + + if leftPrecedent != nil { + + leftStage, err = leftPrecedent(stream) + if err != nil { + return nil, err + } + } + + for stream.hasNext() { + + token = stream.next() + + if len(validKinds) > 0 { + + keyFound = false + for _, kind := range validKinds { + if kind == token.Kind { + keyFound = true + break + } + } + + if !keyFound { + break + } + } + + if validSymbols != nil { + + if !isString(token.Value) { + break + } + + symbol, keyFound = validSymbols[token.Value.(string)] + if !keyFound { + break + } + } + + if rightPrecedent != nil { + rightStage, err = rightPrecedent(stream) + if err != nil { + return nil, err + } + } + + checks = findTypeChecks(symbol) + + return &evaluationStage{ + + symbol: symbol, + leftStage: leftStage, + rightStage: rightStage, + operator: stageSymbolMap[symbol], + + leftTypeCheck: checks.left, + rightTypeCheck: checks.right, + typeCheck: checks.combined, + typeErrorFormat: typeErrorFormat, + }, nil + } + + stream.rewind() + return leftStage, nil +} + +/* + A special case where functions need to be of higher precedence than values, and need a special wrapped execution stage operator. +*/ +func planFunction(stream *tokenStream) (*evaluationStage, error) { + + var token ExpressionToken + var rightStage *evaluationStage + var err error + + token = stream.next() + + if token.Kind != FUNCTION { + stream.rewind() + return planAccessor(stream) + } + + rightStage, err = planAccessor(stream) + if err != nil { + return nil, err + } + + return &evaluationStage{ + + symbol: FUNCTIONAL, + rightStage: rightStage, + operator: makeFunctionStage(token.Value.(ExpressionFunction)), + typeErrorFormat: "Unable to run function '%v': %v", + }, nil +} + +func planAccessor(stream *tokenStream) (*evaluationStage, error) { + + var token, otherToken ExpressionToken + var rightStage *evaluationStage + var err error + + if !stream.hasNext() { + return nil, nil + } + + token = stream.next() + + if token.Kind != ACCESSOR { + stream.rewind() + return planValue(stream) + } + + // check if this is meant to be a function or a field. + // fields have a clause next to them, functions do not. + // if it's a function, parse the arguments. Otherwise leave the right stage null. + if stream.hasNext() { + + otherToken = stream.next() + if otherToken.Kind == CLAUSE { + + stream.rewind() + + rightStage, err = planTokens(stream) + if err != nil { + return nil, err + } + } else { + stream.rewind() + } + } + + return &evaluationStage{ + + symbol: ACCESS, + rightStage: rightStage, + operator: makeAccessorStage(token.Value.([]string)), + typeErrorFormat: "Unable to access parameter field or method '%v': %v", + }, nil +} + +/* + A truly special precedence function, this handles all the "lowest-case" errata of the process, including literals, parmeters, + clauses, and prefixes. +*/ +func planValue(stream *tokenStream) (*evaluationStage, error) { + + var token ExpressionToken + var symbol OperatorSymbol + var ret *evaluationStage + var operator evaluationOperator + var err error + + if !stream.hasNext() { + return nil, nil + } + + token = stream.next() + + switch token.Kind { + + case CLAUSE: + + ret, err = planTokens(stream) + if err != nil { + return nil, err + } + + // advance past the CLAUSE_CLOSE token. We know that it's a CLAUSE_CLOSE, because at parse-time we check for unbalanced parens. + stream.next() + + // the stage we got represents all of the logic contained within the parens + // but for technical reasons, we need to wrap this stage in a "noop" stage which breaks long chains of precedence. + // see github #33. + ret = &evaluationStage{ + rightStage: ret, + operator: noopStageRight, + symbol: NOOP, + } + + return ret, nil + + case CLAUSE_CLOSE: + + // when functions have empty params, this will be hit. In this case, we don't have any evaluation stage to do, + // so we just return nil so that the stage planner continues on its way. + stream.rewind() + return nil, nil + + case VARIABLE: + operator = makeParameterStage(token.Value.(string)) + + case NUMERIC: + fallthrough + case STRING: + fallthrough + case PATTERN: + fallthrough + case BOOLEAN: + symbol = LITERAL + operator = makeLiteralStage(token.Value) + case TIME: + symbol = LITERAL + operator = makeLiteralStage(float64(token.Value.(time.Time).Unix())) + + case PREFIX: + stream.rewind() + return planPrefix(stream) + } + + if operator == nil { + errorMsg := fmt.Sprintf("Unable to plan token kind: '%s', value: '%v'", token.Kind.String(), token.Value) + return nil, errors.New(errorMsg) + } + + return &evaluationStage{ + symbol: symbol, + operator: operator, + }, nil +} + +/* + Convenience function to pass a triplet of typechecks between `findTypeChecks` and `planPrecedenceLevel`. + Each of these members may be nil, which indicates that type does not matter for that value. +*/ +type typeChecks struct { + left stageTypeCheck + right stageTypeCheck + combined stageCombinedTypeCheck +} + +/* + Maps a given [symbol] to a set of typechecks to be used during runtime. +*/ +func findTypeChecks(symbol OperatorSymbol) typeChecks { + + switch symbol { + case GT: + fallthrough + case LT: + fallthrough + case GTE: + fallthrough + case LTE: + return typeChecks{ + combined: comparatorTypeCheck, + } + case REQ: + fallthrough + case NREQ: + return typeChecks{ + left: isString, + right: isRegexOrString, + } + case AND: + fallthrough + case OR: + return typeChecks{ + left: isBool, + right: isBool, + } + case IN: + return typeChecks{ + right: isArray, + } + case BITWISE_LSHIFT: + fallthrough + case BITWISE_RSHIFT: + fallthrough + case BITWISE_OR: + fallthrough + case BITWISE_AND: + fallthrough + case BITWISE_XOR: + return typeChecks{ + left: isFloat64, + right: isFloat64, + } + case PLUS: + return typeChecks{ + combined: additionTypeCheck, + } + case MINUS: + fallthrough + case MULTIPLY: + fallthrough + case DIVIDE: + fallthrough + case MODULUS: + fallthrough + case EXPONENT: + return typeChecks{ + left: isFloat64, + right: isFloat64, + } + case NEGATE: + return typeChecks{ + right: isFloat64, + } + case INVERT: + return typeChecks{ + right: isBool, + } + case BITWISE_NOT: + return typeChecks{ + right: isFloat64, + } + case TERNARY_TRUE: + return typeChecks{ + left: isBool, + } + + // unchecked cases + case EQ: + fallthrough + case NEQ: + return typeChecks{} + case TERNARY_FALSE: + fallthrough + case COALESCE: + fallthrough + default: + return typeChecks{} + } +} + +/* + During stage planning, stages of equal precedence are parsed such that they'll be evaluated in reverse order. + For commutative operators like "+" or "-", it's no big deal. But for order-specific operators, it ruins the expected result. +*/ +func reorderStages(rootStage *evaluationStage) { + + // traverse every rightStage until we find multiples in a row of the same precedence. + var identicalPrecedences []*evaluationStage + var currentStage, nextStage *evaluationStage + var precedence, currentPrecedence operatorPrecedence + + nextStage = rootStage + precedence = findOperatorPrecedenceForSymbol(rootStage.symbol) + + for nextStage != nil { + + currentStage = nextStage + nextStage = currentStage.rightStage + + // left depth first, since this entire method only looks for precedences down the right side of the tree + if currentStage.leftStage != nil { + reorderStages(currentStage.leftStage) + } + + currentPrecedence = findOperatorPrecedenceForSymbol(currentStage.symbol) + + if currentPrecedence == precedence { + identicalPrecedences = append(identicalPrecedences, currentStage) + continue + } + + // precedence break. + // See how many in a row we had, and reorder if there's more than one. + if len(identicalPrecedences) > 1 { + mirrorStageSubtree(identicalPrecedences) + } + + identicalPrecedences = []*evaluationStage{currentStage} + precedence = currentPrecedence + } + + if len(identicalPrecedences) > 1 { + mirrorStageSubtree(identicalPrecedences) + } +} + +/* + Performs a "mirror" on a subtree of stages. + This mirror functionally inverts the order of execution for all members of the [stages] list. + That list is assumed to be a root-to-leaf (ordered) list of evaluation stages, where each is a right-hand stage of the last. +*/ +func mirrorStageSubtree(stages []*evaluationStage) { + + var rootStage, inverseStage, carryStage, frontStage *evaluationStage + + stagesLength := len(stages) + + // reverse all right/left + for _, frontStage = range stages { + + carryStage = frontStage.rightStage + frontStage.rightStage = frontStage.leftStage + frontStage.leftStage = carryStage + } + + // end left swaps with root right + rootStage = stages[0] + frontStage = stages[stagesLength-1] + + carryStage = frontStage.leftStage + frontStage.leftStage = rootStage.rightStage + rootStage.rightStage = carryStage + + // for all non-root non-end stages, right is swapped with inverse stage right in list + for i := 0; i < (stagesLength-2)/2+1; i++ { + + frontStage = stages[i+1] + inverseStage = stages[stagesLength-i-1] + + carryStage = frontStage.rightStage + frontStage.rightStage = inverseStage.rightStage + inverseStage.rightStage = carryStage + } + + // swap all other information with inverse stages + for i := 0; i < stagesLength/2; i++ { + + frontStage = stages[i] + inverseStage = stages[stagesLength-i-1] + frontStage.swapWith(inverseStage) + } +} + +/* + Recurses through all operators in the entire tree, eliding operators where both sides are literals. +*/ +func elideLiterals(root *evaluationStage) *evaluationStage { + + if root.leftStage != nil { + root.leftStage = elideLiterals(root.leftStage) + } + + if root.rightStage != nil { + root.rightStage = elideLiterals(root.rightStage) + } + + return elideStage(root) +} + +/* + Elides a specific stage, if possible. + Returns the unmodified [root] stage if it cannot or should not be elided. + Otherwise, returns a new stage representing the condensed value from the elided stages. +*/ +func elideStage(root *evaluationStage) *evaluationStage { + + var leftValue, rightValue, result interface{} + var err error + + // right side must be a non-nil value. Left side must be nil or a value. + if root.rightStage == nil || + root.rightStage.symbol != LITERAL || + root.leftStage == nil || + root.leftStage.symbol != LITERAL { + return root + } + + // don't elide some operators + switch root.symbol { + case SEPARATE: + fallthrough + case IN: + return root + } + + // both sides are values, get their actual values. + // errors should be near-impossible here. If we encounter them, just abort this optimization. + leftValue, err = root.leftStage.operator(nil, nil, nil) + if err != nil { + return root + } + + rightValue, err = root.rightStage.operator(nil, nil, nil) + if err != nil { + return root + } + + // typcheck, since the grammar checker is a bit loose with which operator symbols go together. + err = typeCheck(root.leftTypeCheck, leftValue, root.symbol, root.typeErrorFormat) + if err != nil { + return root + } + + err = typeCheck(root.rightTypeCheck, rightValue, root.symbol, root.typeErrorFormat) + if err != nil { + return root + } + + if root.typeCheck != nil && !root.typeCheck(leftValue, rightValue) { + return root + } + + // pre-calculate, and return a new stage representing the result. + result, err = root.operator(leftValue, rightValue, nil) + if err != nil { + return root + } + + return &evaluationStage{ + symbol: LITERAL, + operator: makeLiteralStage(result), + } +} diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/test.sh b/image-scanner/vendor/github.com/Knetic/govaluate/test.sh new file mode 100644 index 000000000..11aa8b332 --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/test.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Script that runs tests, code coverage, and benchmarks all at once. +# Builds a symlink in /tmp, mostly to avoid messing with GOPATH at the user's shell level. + +TEMPORARY_PATH="/tmp/govaluate_test" +SRC_PATH="${TEMPORARY_PATH}/src" +FULL_PATH="${TEMPORARY_PATH}/src/govaluate" + +# set up temporary directory +rm -rf "${FULL_PATH}" +mkdir -p "${SRC_PATH}" + +ln -s $(pwd) "${FULL_PATH}" +export GOPATH="${TEMPORARY_PATH}" + +pushd "${TEMPORARY_PATH}/src/govaluate" + +# run the actual tests. +export GOVALUATE_TORTURE_TEST="true" +go test -bench=. -benchmem #-coverprofile coverage.out +status=$? + +if [ "${status}" != 0 ]; +then + exit $status +fi + +# coverage +# disabled because travis go1.4 seems not to support it suddenly? +#go tool cover -func=coverage.out + +popd diff --git a/image-scanner/vendor/github.com/Knetic/govaluate/tokenStream.go b/image-scanner/vendor/github.com/Knetic/govaluate/tokenStream.go new file mode 100644 index 000000000..d0029209d --- /dev/null +++ b/image-scanner/vendor/github.com/Knetic/govaluate/tokenStream.go @@ -0,0 +1,36 @@ +package govaluate + +type tokenStream struct { + tokens []ExpressionToken + index int + tokenLength int +} + +func newTokenStream(tokens []ExpressionToken) *tokenStream { + + var ret *tokenStream + + ret = new(tokenStream) + ret.tokens = tokens + ret.tokenLength = len(tokens) + return ret +} + +func (this *tokenStream) rewind() { + this.index -= 1 +} + +func (this *tokenStream) next() ExpressionToken { + + var token ExpressionToken + + token = this.tokens[this.index] + + this.index += 1 + return token +} + +func (this tokenStream) hasNext() bool { + + return this.index < this.tokenLength +} diff --git a/image-scanner/vendor/github.com/antihax/optional/LICENSE b/image-scanner/vendor/github.com/antihax/optional/LICENSE new file mode 100644 index 000000000..19587fe47 --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/LICENSE @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2016 Adam Hintz + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/image-scanner/vendor/github.com/antihax/optional/bool.go b/image-scanner/vendor/github.com/antihax/optional/bool.go new file mode 100644 index 000000000..4f35ff561 --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/bool.go @@ -0,0 +1,36 @@ +package optional + +type Bool struct { + isSet bool + value bool +} + +func NewBool(value bool) Bool { + return Bool{ + true, + value, + } +} + +// EmptyBool returns a new Bool that does not have a value set. +func EmptyBool() Bool { + return Bool{ + false, + false, + } +} + +func (b Bool) IsSet() bool { + return b.isSet +} + +func (b Bool) Value() bool { + return b.value +} + +func (b Bool) Default(defaultValue bool) bool { + if b.isSet { + return b.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/byte.go b/image-scanner/vendor/github.com/antihax/optional/byte.go new file mode 100644 index 000000000..d22bbe8ff --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/byte.go @@ -0,0 +1,36 @@ +package optional + +type Byte struct { + isSet bool + value byte +} + +func NewByte(value byte) Byte { + return Byte{ + true, + value, + } +} + +// EmptyByte returns a new Byte that does not have a value set. +func EmptyByte() Byte { + return Byte{ + false, + 0, + } +} + +func (b Byte) IsSet() bool { + return b.isSet +} + +func (b Byte) Value() byte { + return b.value +} + +func (b Byte) Default(defaultValue byte) byte { + if b.isSet { + return b.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/complex128.go b/image-scanner/vendor/github.com/antihax/optional/complex128.go new file mode 100644 index 000000000..290b3f37b --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/complex128.go @@ -0,0 +1,36 @@ +package optional + +type Complex128 struct { + isSet bool + value complex128 +} + +func NewComplex128(value complex128) Complex128 { + return Complex128{ + true, + value, + } +} + +// EmptyComplex128 returns a new Complex128 that does not have a value set. +func EmptyComplex128() Complex128 { + return Complex128{ + false, + 0, + } +} + +func (i Complex128) IsSet() bool { + return i.isSet +} + +func (i Complex128) Value() complex128 { + return i.value +} + +func (i Complex128) Default(defaultValue complex128) complex128 { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/complex64.go b/image-scanner/vendor/github.com/antihax/optional/complex64.go new file mode 100644 index 000000000..7d372d8c1 --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/complex64.go @@ -0,0 +1,36 @@ +package optional + +type Complex64 struct { + isSet bool + value complex64 +} + +func NewComplex64(value complex64) Complex64 { + return Complex64{ + true, + value, + } +} + +// EmptyComplex64 returns a new Complex64 that does not have a value set. +func EmptyComplex64() Complex64 { + return Complex64{ + false, + 0, + } +} + +func (i Complex64) IsSet() bool { + return i.isSet +} + +func (i Complex64) Value() complex64 { + return i.value +} + +func (i Complex64) Default(defaultValue complex64) complex64 { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/float32.go b/image-scanner/vendor/github.com/antihax/optional/float32.go new file mode 100644 index 000000000..519cc5d69 --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/float32.go @@ -0,0 +1,36 @@ +package optional + +type Float32 struct { + isSet bool + value float32 +} + +func NewFloat32(value float32) Float32 { + return Float32{ + true, + value, + } +} + +// EmptyFloat32 returns a new Float32 that does not have a value set. +func EmptyFloat32() Float32 { + return Float32{ + false, + 0, + } +} + +func (i Float32) IsSet() bool { + return i.isSet +} + +func (i Float32) Value() float32 { + return i.value +} + +func (i Float32) Default(defaultValue float32) float32 { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/float64.go b/image-scanner/vendor/github.com/antihax/optional/float64.go new file mode 100644 index 000000000..835f7058e --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/float64.go @@ -0,0 +1,36 @@ +package optional + +type Float64 struct { + isSet bool + value float64 +} + +func NewFloat64(value float64) Float64 { + return Float64{ + true, + value, + } +} + +// EmptyFloat64 returns a new Float64 that does not have a value set. +func EmptyFloat64() Float64 { + return Float64{ + false, + 0, + } +} + +func (i Float64) IsSet() bool { + return i.isSet +} + +func (i Float64) Value() float64 { + return i.value +} + +func (i Float64) Default(defaultValue float64) float64 { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/int.go b/image-scanner/vendor/github.com/antihax/optional/int.go new file mode 100644 index 000000000..6c3e65b92 --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/int.go @@ -0,0 +1,36 @@ +package optional + +type Int struct { + isSet bool + value int +} + +func NewInt(value int) Int { + return Int{ + true, + value, + } +} + +// EmptyInt returns a new Int that does not have a value set. +func EmptyInt() Int { + return Int{ + false, + 0, + } +} + +func (i Int) IsSet() bool { + return i.isSet +} + +func (i Int) Value() int { + return i.value +} + +func (i Int) Default(defaultValue int) int { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/int16.go b/image-scanner/vendor/github.com/antihax/optional/int16.go new file mode 100644 index 000000000..49465b8c7 --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/int16.go @@ -0,0 +1,36 @@ +package optional + +type Int16 struct { + isSet bool + value int16 +} + +func NewInt16(value int16) Int16 { + return Int16{ + true, + value, + } +} + +// EmptyInt16 returns a new Int16 that does not have a value set. +func EmptyInt16() Int16 { + return Int16{ + false, + 0, + } +} + +func (i Int16) IsSet() bool { + return i.isSet +} + +func (i Int16) Value() int16 { + return i.value +} + +func (i Int16) Default(defaultValue int16) int16 { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/int32.go b/image-scanner/vendor/github.com/antihax/optional/int32.go new file mode 100644 index 000000000..f3a33c78d --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/int32.go @@ -0,0 +1,36 @@ +package optional + +type Int32 struct { + isSet bool + value int32 +} + +func NewInt32(value int32) Int32 { + return Int32{ + true, + value, + } +} + +// EmptyInt32 returns a new Int32 that does not have a value set. +func EmptyInt32() Int32 { + return Int32{ + false, + 0, + } +} + +func (i Int32) IsSet() bool { + return i.isSet +} + +func (i Int32) Value() int32 { + return i.value +} + +func (i Int32) Default(defaultValue int32) int32 { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/int64.go b/image-scanner/vendor/github.com/antihax/optional/int64.go new file mode 100644 index 000000000..d047470b7 --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/int64.go @@ -0,0 +1,36 @@ +package optional + +type Int64 struct { + isSet bool + value int64 +} + +func NewInt64(value int64) Int64 { + return Int64{ + true, + value, + } +} + +// EmptyInt64 returns a new Int64 that does not have a value set. +func EmptyInt64() Int64 { + return Int64{ + false, + 0, + } +} + +func (i Int64) IsSet() bool { + return i.isSet +} + +func (i Int64) Value() int64 { + return i.value +} + +func (i Int64) Default(defaultValue int64) int64 { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/int8.go b/image-scanner/vendor/github.com/antihax/optional/int8.go new file mode 100644 index 000000000..2bd11bda7 --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/int8.go @@ -0,0 +1,36 @@ +package optional + +type Int8 struct { + isSet bool + value int8 +} + +func NewInt8(value int8) Int8 { + return Int8{ + true, + value, + } +} + +// EmptyInt8 returns a new Int8 that does not have a value set. +func EmptyInt8() Int8 { + return Int8{ + false, + 0, + } +} + +func (i Int8) IsSet() bool { + return i.isSet +} + +func (i Int8) Value() int8 { + return i.value +} + +func (i Int8) Default(defaultValue int8) int8 { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/interface.go b/image-scanner/vendor/github.com/antihax/optional/interface.go new file mode 100644 index 000000000..a6f2416dd --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/interface.go @@ -0,0 +1,37 @@ +package optional + +// Optional represents a generic optional type, stored as an interface{}. +type Interface struct { + isSet bool + value interface{} +} + +func NewInterface(value interface{}) Interface { + return Interface{ + true, + value, + } +} + +// EmptyInterface returns a new Interface that does not have a value set. +func EmptyInterface() Interface { + return Interface{ + false, + nil, + } +} + +func (b Interface) IsSet() bool { + return b.isSet +} + +func (b Interface) Value() interface{} { + return b.value +} + +func (b Interface) Default(defaultValue interface{}) interface{} { + if b.isSet { + return b.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/rune.go b/image-scanner/vendor/github.com/antihax/optional/rune.go new file mode 100644 index 000000000..cbac81929 --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/rune.go @@ -0,0 +1,36 @@ +package optional + +type Rune struct { + isSet bool + value rune +} + +func NewRune(value rune) Rune { + return Rune{ + true, + value, + } +} + +// EmptyRune returns a new Rune that does not have a value set. +func EmptyRune() Rune { + return Rune{ + false, + 0, + } +} + +func (b Rune) IsSet() bool { + return b.isSet +} + +func (b Rune) Value() rune { + return b.value +} + +func (b Rune) Default(defaultValue rune) rune { + if b.isSet { + return b.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/string.go b/image-scanner/vendor/github.com/antihax/optional/string.go new file mode 100644 index 000000000..a2e5a2b44 --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/string.go @@ -0,0 +1,36 @@ +package optional + +type String struct { + isSet bool + value string +} + +func NewString(value string) String { + return String{ + true, + value, + } +} + +// EmptyString returns a new String that does not have a value set. +func EmptyString() String { + return String{ + false, + "", + } +} + +func (b String) IsSet() bool { + return b.isSet +} + +func (b String) Value() string { + return b.value +} + +func (b String) Default(defaultValue string) string { + if b.isSet { + return b.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/time.go b/image-scanner/vendor/github.com/antihax/optional/time.go new file mode 100644 index 000000000..a92a55aa4 --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/time.go @@ -0,0 +1,38 @@ +package optional + +import "time" + +type Time struct { + isSet bool + value time.Time +} + +func NewTime(value time.Time) Time { + return Time{ + true, + value, + } +} + +// EmptyTime returns a new Time that does not have a value set. +func EmptyTime() Time { + return Time{ + false, + time.Time{}, + } +} + +func (b Time) IsSet() bool { + return b.isSet +} + +func (b Time) Value() time.Time { + return b.value +} + +func (b Time) Default(defaultValue time.Time) time.Time { + if b.isSet { + return b.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/uint.go b/image-scanner/vendor/github.com/antihax/optional/uint.go new file mode 100644 index 000000000..f9e53a41b --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/uint.go @@ -0,0 +1,36 @@ +package optional + +type Uint struct { + isSet bool + value uint +} + +func NewUint(value uint) Uint { + return Uint{ + true, + value, + } +} + +// EmptyUint returns a new Uint that does not have a value set. +func EmptyUint() Uint { + return Uint{ + false, + 0, + } +} + +func (i Uint) IsSet() bool { + return i.isSet +} + +func (i Uint) Value() uint { + return i.value +} + +func (i Uint) Default(defaultValue uint) uint { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/uint16.go b/image-scanner/vendor/github.com/antihax/optional/uint16.go new file mode 100644 index 000000000..487eb673b --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/uint16.go @@ -0,0 +1,36 @@ +package optional + +type Uint16 struct { + isSet bool + value uint16 +} + +func NewUint16(value uint16) Uint16 { + return Uint16{ + true, + value, + } +} + +// EmptyUint16 returns a new Uint16 that does not have a value set. +func EmptyUint16() Uint16 { + return Uint16{ + false, + 0, + } +} + +func (i Uint16) IsSet() bool { + return i.isSet +} + +func (i Uint16) Value() uint16 { + return i.value +} + +func (i Uint16) Default(defaultValue uint16) uint16 { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/uint32.go b/image-scanner/vendor/github.com/antihax/optional/uint32.go new file mode 100644 index 000000000..0e46c8378 --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/uint32.go @@ -0,0 +1,36 @@ +package optional + +type Uint32 struct { + isSet bool + value uint32 +} + +func NewUint32(value uint32) Uint32 { + return Uint32{ + true, + value, + } +} + +// EmptyUint32 returns a new Uint32 that does not have a value set. +func EmptyUint32() Uint32 { + return Uint32{ + false, + 0, + } +} + +func (i Uint32) IsSet() bool { + return i.isSet +} + +func (i Uint32) Value() uint32 { + return i.value +} + +func (i Uint32) Default(defaultValue uint32) uint32 { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/uint64.go b/image-scanner/vendor/github.com/antihax/optional/uint64.go new file mode 100644 index 000000000..16004df34 --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/uint64.go @@ -0,0 +1,36 @@ +package optional + +type Uint64 struct { + isSet bool + value uint64 +} + +func NewUint64(value uint64) Uint64 { + return Uint64{ + true, + value, + } +} + +// EmptyUint64 returns a new Uint64 that does not have a value set. +func EmptyUint64() Uint64 { + return Uint64{ + false, + 0, + } +} + +func (i Uint64) IsSet() bool { + return i.isSet +} + +func (i Uint64) Value() uint64 { + return i.value +} + +func (i Uint64) Default(defaultValue uint64) uint64 { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/uint8.go b/image-scanner/vendor/github.com/antihax/optional/uint8.go new file mode 100644 index 000000000..469ceb53a --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/uint8.go @@ -0,0 +1,36 @@ +package optional + +type Uint8 struct { + isSet bool + value uint8 +} + +func NewUint8(value uint8) Uint8 { + return Uint8{ + true, + value, + } +} + +// EmptyUint8 returns a new Uint8 that does not have a value set. +func EmptyUint8() Uint8 { + return Uint8{ + false, + 0, + } +} + +func (i Uint8) IsSet() bool { + return i.isSet +} + +func (i Uint8) Value() uint8 { + return i.value +} + +func (i Uint8) Default(defaultValue uint8) uint8 { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/antihax/optional/uintptr.go b/image-scanner/vendor/github.com/antihax/optional/uintptr.go new file mode 100644 index 000000000..e7e2cabe0 --- /dev/null +++ b/image-scanner/vendor/github.com/antihax/optional/uintptr.go @@ -0,0 +1,36 @@ +package optional + +type Uintptr struct { + isSet bool + value uintptr +} + +func NewUintptr(value uintptr) Uintptr { + return Uintptr{ + true, + value, + } +} + +// EmptyUintptr returns a new Uintptr that does not have a value set. +func EmptyUintptr() Uintptr { + return Uintptr{ + false, + 0, + } +} + +func (i Uintptr) IsSet() bool { + return i.isSet +} + +func (i Uintptr) Value() uintptr { + return i.value +} + +func (i Uintptr) Default(defaultValue uintptr) uintptr { + if i.isSet { + return i.value + } + return defaultValue +} diff --git a/image-scanner/vendor/github.com/arl/statsviz/.gitattributes b/image-scanner/vendor/github.com/arl/statsviz/.gitattributes new file mode 100644 index 000000000..9c6c84822 --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/.gitattributes @@ -0,0 +1 @@ +internal/static/** linguist-detectable=false diff --git a/image-scanner/vendor/github.com/arl/statsviz/.gitignore b/image-scanner/vendor/github.com/arl/statsviz/.gitignore new file mode 100644 index 000000000..befa1383f --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/.gitignore @@ -0,0 +1,2 @@ +*.test +out diff --git a/image-scanner/vendor/github.com/arl/statsviz/CHANGELOG.md b/image-scanner/vendor/github.com/arl/statsviz/CHANGELOG.md new file mode 100644 index 000000000..7930ee610 --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/CHANGELOG.md @@ -0,0 +1,85 @@ +v0.6.0 / 2023-10-08 +============== + * New plots showing new go1.20/go1.21 runtime/metrics: (#111) + + GC Cycles + + GC Scan + + Memory classes + + CPU classes + + Mutex wait + * Users can now add their own plots (#111) + * Add light/dark mode selector (#108) + +v0.5.2 / 2023-03-29 +============== + * Ensure all files have a correct Content-Type (#106) + +v0.5.1 / 2022-09-30 +============== + * Fix UI on Firefox (#83) + +v0.5.0 / 2022-09-05 +============== + * Switch to runtime/metrics as source, major refactor (#75) + + New heatmap UI component + + Dynamic plots definition based on server side generated config + + Add many new plots (scheduler latency, scheduling events, and more) + + Add play/pause switch button + + Add show/hide GC events switch button + + Add time range selector (1m, 5m, 10m) + * Switch javascript code to ES6 (#65) + * Build and test all examples (#63) + +v0.4.1 / 2021-12-12 +============== + * Assets are `go:embed`ed, so the minimum go version is now go1.16 (#55) + * Polishing (README, small UI improvements) (#54) + * Small ui improvements: link to go.dev rather than golang.org + +v0.4.0 / 2021-05-08 +================== + + * Auto-reconnect to new server from GUI after closed websocket connection (#49) + * Reorganize examples (#51) + * Make `IndexAtRoot` returns an `http.HandlerFunc` instead of `http.Handler` (#52) + +v0.3.0 / 2021-02-14 +================== + + * Enable 'save as png' button on plots (#44) + +v0.2.2 / 2020-12-13 +================== + + * Use Go Modules for 'github.com/gorilla/websocket' (#39) + * Support custom frequency (#37) + * Added fixed go-chi example (#38) + * `_example`: add echo (#22) + * `_example`: add gin example (#34) + * ci: track coverage + * RegisterDefault returns an error now + * Ensure send frequency is a strictly positive integer + * Don't log if we can't upgrade to websocket + * `_example`_example: add chi router (#38) + * `_example`_example: change structure to have one example per directory + +v0.2.1 / 2020-10-29 +=================== + + * Fix websocket handler now working with https (#25) + +v0.2.0 / 2020-10-25 +=================== + + * `Register` now accepts options (functional options API) (#20) + + `Root` allows to root statsviz at a path different than `/debug/statsviz` + + `SendFrequency` allows to set the frequency at which stats are emitted. + +v0.1.1 / 2020-10-12 +=================== + + * Do not leak timer in sendStats + +v0.1.0 / 2020-10-10 +=================== + + * First released version diff --git a/image-scanner/vendor/github.com/arl/statsviz/CONTRIBUTING.md b/image-scanner/vendor/github.com/arl/statsviz/CONTRIBUTING.md new file mode 100644 index 000000000..048b206f3 --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/CONTRIBUTING.md @@ -0,0 +1,66 @@ +Contributing +============ + +First of all, thank you for considering to contribute to Statsviz! + +Pull-requests are welcome! + + +## Go library + +The Statsviz Go public API is relatively light so there's not much to do and at +the moment it's unlikely that the API will change. However new options can be +added to `statsviz.Register` and `statsviz.NewServer` without breaking +compatibility. + +That being said, there may be things to improve in the implementation, any +contribution is very welcome! + +Big changes should be discussed on the issue tracker prior to start working on +the code. + +If you've decided to contribute, thank you so much, please comment on the existing +issue or create one stating what you want to tackle and why. + + +## User interface (html/css/javascript) + +The user interface aims to be simple, light and minimal. + +Assets are located in the `internal/static` directory and are embedded with +[`go:embed`](https://pkg.go.dev/embed). + +Depending on what your modifications are, it's always a good idea to check that +some of the examples in [./_example](./_example/) work with your modifications +to Statsviz. To do so `cd` to the directory of the example and run: + + go mod edit -replace=github.com/arl/statsviz=../../ + + +## Documentation + +No contribution is too small, improvements to code comments and/or README +are welcome! + + +## Examples + +There are many Go libraries to handle HTTP requests, routing, etc.. + +Feel free to add an example to show how to register Statsviz with your favourite +library. + +To do so, please add a directory under `./_example`. For instance, if you want to add an +example showing how to register Statsviz within library `foobar`: + + - create a directory `./_example/foobar/` + - create a file `./_example/foobar/main.go` + - call `go example.Work()` as the first line of your example (see other + examples). This forces the garbage collector to _do something_ so that + Statsviz interface won't remain static when an user runs your example. + - the code should be `gofmt`ed + - the example should compile and run + - when ran, Statsviz interface should be accessible at http://localhost:8080/debug/statsviz + + +Thank you! diff --git a/image-scanner/vendor/github.com/arl/statsviz/LICENSE b/image-scanner/vendor/github.com/arl/statsviz/LICENSE new file mode 100644 index 000000000..3cc5a5bfd --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Aurélien Rainone + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/image-scanner/vendor/github.com/arl/statsviz/README.md b/image-scanner/vendor/github.com/arl/statsviz/README.md new file mode 100644 index 000000000..496cb6f3d --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/README.md @@ -0,0 +1,230 @@ +[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=round-square)](https://pkg.go.dev/github.com/arl/statsviz) +[![Latest tag](https://img.shields.io/github/tag/arl/statsviz.svg)](https://github.com/arl/statsviz/tag/) +[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go) + +[![Test Actions Status](https://github.com/arl/statsviz/workflows/Tests-linux/badge.svg)](https://github.com/arl/statsviz/actions) +[![Test Actions Status](https://github.com/arl/statsviz/workflows/Tests-others/badge.svg)](https://github.com/arl/statsviz/actions) +[![codecov](https://codecov.io/gh/arl/statsviz/branch/main/graph/badge.svg)](https://codecov.io/gh/arl/statsviz) + +# Statsviz + +

+ Statsviz Gopher Logo + statsviz ui +

+
+ +Visualize real time plots of your Go program runtime metrics, including heap, objects, goroutines, GC pauses, scheduler and more, in your browser. + +
+ +- [Statsviz](#statsviz) + - [Install](#install) + - [Usage](#usage) + - [Advanced Usage](#advanced-usage) + - [How Does That Work?](#how-does-that-work) + - [Documentation](#documentation) + - [Go API](#go-api) + - [User interface](#user-interface) + - [Plots](#plots) + - [User Plots](#user-plots) + - [Examples](#examples) + - [Questions / Troubleshooting](#questions--troubleshooting) + - [Contributing](#contributing) + - [Changelog](#changelog) + - [License: MIT](#license-mit) + +## Install + +Download the latest version: + +``` +go get github.com/arl/statsviz@latest +``` + +Please note that, as new metrics are added to the `/runtime/metrics` package, new plots are added to Statsviz. +This also means that the presence of some plots on the dashboard depends on the Go version you're using. + +When in doubt, use the latest ;-) + + +## Usage + +Register `Statsviz` HTTP handlers with your application `http.ServeMux`. + +```go +mux := http.NewServeMux() +statsviz.Register(mux) + +go func() { + log.Println(http.ListenAndServe("localhost:8080", mux)) +}() +``` + +Open your browser at http://localhost:8080/debug/statsviz + + +## Advanced Usage + +If you want more control over Statsviz HTTP handlers, examples are: + - you're using some HTTP framework + - you want to place Statsviz handler behind some middleware + +then use `statsviz.NewServer` to obtain a `Server` instance. Both the `Index()` and `Ws()` methods return `http.HandlerFunc`. + +```go +srv, err := statsviz.NewServer(); // Create server or handle error +srv.Index() // UI (dashboard) http.HandlerFunc +srv.Ws() // Websocket http.HandlerFunc +``` + +Please look at examples of usage in the [Examples](_example) directory. + + +## How Does That Work? + +`statsviz.Register` registers 2 HTTP handlers within the given `http.ServeMux`: + +- the `Index` handler serves Statsviz user interface at `/debug/statsviz` at the address served by your program. + +- The `Ws` serves a Websocket endpoint. When the browser connects to that endpoint, [runtime/metrics](https://pkg.go.dev/runtime/metrics) are sent to the browser, once per second. + +Data points are in a browser-side circular-buffer. + + +## Documentation + +### Go API + +Check out the API reference on [pkg.go.dev](https://pkg.go.dev/github.com/arl/statsviz#section-documentation). + +### User interface + +Controls at the top of the page act on all plots: + +menu + +- the groom shows/hides the vertical lines representing garbage collections. +- the time range selector defines the visualized time span. +- the play/pause icons stops and resume the refresh of the plots. +- the light/dark selector switches between light and dark modes. + +On top of each plot there are 2 icons: + +menu + +- the camera downloads a PNG image of the plot. +- the info icon shows details about the metrics displayed. + +### Plots + +Depending on your go version, some plots may not be available. + +#### Heap (global) + +heap-global + +#### Heap (details) + +heap-details + +#### Live Objects in Heap + +live-objects + +#### Live Bytes in Heap + +live-bytes + +#### MSpan/MCache + +mspan-mcache + +#### Memory classes + +memory-classes + +#### Goroutines + +goroutines + +#### Size Classes + +size-classes + +#### GC Scan + +gc-scan + +#### GC Cycles + +gc-cycles + +#### Stop-the-world Pause Latencies + +gc-pauses + +#### CPU Classes (GC) + +cpu-classes-gc + +#### Time Goroutines Spend in 'Runnable' state + +runnable-time + +#### Time Goroutines Spend Blocked on Mutexes + +mutex-wait + +#### Starting Size of Goroutines Stacks + +gc-stack-size + +#### Goroutine Scheduling Events + +sched-events + +#### CGO Calls + +cgo + + +### User Plots + +Since `v0.6` you can add your own plots to Statsviz dashboard, in order to easily +visualize your application metrics next to runtime metrics. + +Please see the [userplots example](_example/userplots/main.go). + +## Examples + +Check out the [\_example](./_example/README.md) directory to see various ways to use Statsviz, such as: + +- use of `http.DefaultServeMux` or your own `http.ServeMux` +- wrap HTTP handler behind a middleware +- register the web page at `/foo/bar` instead of `/debug/statsviz` +- use `https://` rather than `http://` +- register Statsviz handlers with various Go HTTP libraries/frameworks: + - [echo](https://github.com/labstack/echo/) + - [fasthttp](https://github.com/valyala/fasthttp) + - [fiber](https://github.com/gofiber/fiber/) + - [gin](https://github.com/gin-gonic/gin) + - and many others thanks to many contributors! + +## Questions / Troubleshooting + +Either use GitHub's [discussions](https://github.com/arl/statsviz/discussions) or come to say hi and ask a live question on [#statsviz channel on Gopher's slack](https://gophers.slack.com/archives/C043DU4NZ9D). + +## Contributing + +Please use [issues](https://github.com/arl/statsviz/issues/new/choose) for bugs and feature requests. +Pull-requests are always welcome! +More details in [CONTRIBUTING.md](CONTRIBUTING.md). + +## Changelog + +See [CHANGELOG.md](./CHANGELOG.md). + +## License: MIT + +See [LICENSE](LICENSE) diff --git a/image-scanner/vendor/github.com/arl/statsviz/codecov.yml b/image-scanner/vendor/github.com/arl/statsviz/codecov.yml new file mode 100644 index 000000000..e6829ed7d --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/codecov.yml @@ -0,0 +1,14 @@ +codecov: + require_ci_to_pass: yes + +coverage: + round: down + precision: 2 + + status: + project: + default: + informational: true + patch: + default: + informational: true diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/plot/color.go b/image-scanner/vendor/github.com/arl/statsviz/internal/plot/color.go new file mode 100644 index 000000000..6366fc20f --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/plot/color.go @@ -0,0 +1,64 @@ +package plot + +import ( + "fmt" + "image/color" +) + +func RGBString(r, g, b uint8) string { + return fmt.Sprintf(`"rgb(%d,%d,%d,0)"`, r, g, b) +} + +type WeightedColor struct { + Value float64 + Color color.RGBA +} + +func (c WeightedColor) MarshalJSON() ([]byte, error) { + str := fmt.Sprintf(`[%f,"rgb(%d,%d,%d,%f)"]`, + c.Value, c.Color.R, c.Color.G, c.Color.B, float64(c.Color.A)/255) + return []byte(str), nil +} + +// https://mdigi.tools/color-shades/ +var BlueShades = []WeightedColor{ + {Value: 0.0, Color: color.RGBA{0xea, 0xf8, 0xfd, 1}}, + {Value: 0.1, Color: color.RGBA{0xbf, 0xeb, 0xfa, 1}}, + {Value: 0.2, Color: color.RGBA{0x94, 0xdd, 0xf6, 1}}, + {Value: 0.3, Color: color.RGBA{0x69, 0xd0, 0xf2, 1}}, + {Value: 0.4, Color: color.RGBA{0x3f, 0xc2, 0xef, 1}}, + {Value: 0.5, Color: color.RGBA{0x14, 0xb5, 0xeb, 1}}, + {Value: 0.6, Color: color.RGBA{0x10, 0x94, 0xc0, 1}}, + {Value: 0.7, Color: color.RGBA{0x0d, 0x73, 0x96, 1}}, + {Value: 0.8, Color: color.RGBA{0x09, 0x52, 0x6b, 1}}, + {Value: 0.9, Color: color.RGBA{0x05, 0x31, 0x40, 1}}, + {Value: 1.0, Color: color.RGBA{0x02, 0x10, 0x15, 1}}, +} + +var PinkShades = []WeightedColor{ + {Value: 0.0, Color: color.RGBA{0xfe, 0xe7, 0xf3, 1}}, + {Value: 0.1, Color: color.RGBA{0xfc, 0xb6, 0xdc, 1}}, + {Value: 0.2, Color: color.RGBA{0xf9, 0x85, 0xc5, 1}}, + {Value: 0.3, Color: color.RGBA{0xf7, 0x55, 0xae, 1}}, + {Value: 0.4, Color: color.RGBA{0xf5, 0x24, 0x96, 1}}, + {Value: 0.5, Color: color.RGBA{0xdb, 0x0a, 0x7d, 1}}, + {Value: 0.6, Color: color.RGBA{0xaa, 0x08, 0x61, 1}}, + {Value: 0.7, Color: color.RGBA{0x7a, 0x06, 0x45, 1}}, + {Value: 0.8, Color: color.RGBA{0x49, 0x03, 0x2a, 1}}, + {Value: 0.9, Color: color.RGBA{0x18, 0x01, 0x0e, 1}}, + {Value: 1.0, Color: color.RGBA{0x00, 0x00, 0x00, 1}}, +} + +var GreenShades = []WeightedColor{ + {Value: 0.0, Color: color.RGBA{0xed, 0xf7, 0xf2, 0}}, + {Value: 0.1, Color: color.RGBA{0xc9, 0xe8, 0xd7, 0}}, + {Value: 0.2, Color: color.RGBA{0xa5, 0xd9, 0xbc, 0}}, + {Value: 0.3, Color: color.RGBA{0x81, 0xca, 0xa2, 0}}, + {Value: 0.4, Color: color.RGBA{0x5e, 0xbb, 0x87, 0}}, + {Value: 0.5, Color: color.RGBA{0x44, 0xa1, 0x6e, 0}}, + {Value: 0.6, Color: color.RGBA{0x35, 0x7e, 0x55, 0}}, + {Value: 0.7, Color: color.RGBA{0x26, 0x5a, 0x3d, 0}}, + {Value: 0.8, Color: color.RGBA{0x17, 0x36, 0x25, 0}}, + {Value: 0.9, Color: color.RGBA{0x08, 0x12, 0x0c, 0}}, + {Value: 1.0, Color: color.RGBA{0x00, 0x00, 0x00, 0}}, +} diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/plot/hist.go b/image-scanner/vendor/github.com/arl/statsviz/internal/plot/hist.go new file mode 100644 index 000000000..e46616c1d --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/plot/hist.go @@ -0,0 +1,87 @@ +package plot + +import ( + "math" + "runtime/metrics" +) + +// maxBuckets is the maximum number of buckets we'll plots in heatmaps. +// Histograms with more buckets than that are going to be downsampled. +const maxBuckets = 100 + +// downsampleFactor computes the downsampling factor to use with +// downsampleCounts and downsampleBuckets. nstart and nfinal are the number of +// buckets in the original and final bucket. +func downsampleFactor(norg, nfinal int) int { + mod := norg % nfinal + if mod == 0 { + return norg / nfinal + } + return 1 + norg/nfinal +} + +// downsampleBuckets downsamples the number of buckets in the provided +// histogram, using the given dividing factor, and returns a slice of bucket +// widths. +// +// Given that metrics.Float64Histogram contains the boundaries of histogram +// buckets, the first bucket is not even considered since we're only interested +// in upper bounds. Also, since we can't draw an infinitely large bucket, if h +// last bucket holds +Inf, the width of the last returned bucket will +// extrapolated from the previous 2 buckets. +func downsampleBuckets(h *metrics.Float64Histogram, factor int) []float64 { + var ret []float64 + vals := h.Buckets[1:] + + for i := 0; i < len(vals); i++ { + if (i+1)%factor == 0 { + ret = append(ret, vals[i]) + } + } + if len(vals)%factor != 0 { + // If the number of bucket is not divisible by the factor, let's make a + // last downsampled bucket, even if it doesn't 'contain' the same number + // of original buckets. + ret = append(ret, vals[len(vals)-1]) + } + + if len(ret) > 2 && math.IsInf(ret[len(ret)-1], 1) { + // Plotly doesn't support a +Inf bound for the last bucket. So we make it + // so that the last bucket has the same 'width' than the penultimate one. + ret[len(ret)-1] = ret[len(ret)-2] - ret[len(ret)-3] + ret[len(ret)-2] + } + + return ret +} + +// downsampleCounts downsamples the counts in the provided histogram, using the +// given factor. Every 'factor' buckets are merged into one, larger, bucket. If +// the number of buckets is not divisible by 'factor', then an additional last +// bucket will contain the sum of the counts in all relainbing buckets. +// +// Note: slice should be a slice of maxBuckets elements, so that it can be +// reused across calls. +func downsampleCounts(h *metrics.Float64Histogram, factor int, slice []uint64) []uint64 { + vals := h.Counts + + if factor == 1 { + copy(slice, vals) + slice = slice[:len(vals)] + return slice + } + + slice = slice[:0] + + var sum uint64 + for i := 0; i < len(vals); i++ { + if i%factor == 0 && i > 1 { + slice = append(slice, sum) + sum = vals[i] + } else { + sum += vals[i] + } + } + + // Whatever sum remains, it goes to the last bucket. + return append(slice, sum) +} diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/plot/layout.go b/image-scanner/vendor/github.com/arl/statsviz/internal/plot/layout.go new file mode 100644 index 000000000..bf7122b60 --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/plot/layout.go @@ -0,0 +1,69 @@ +package plot + +type ( + Config struct { + // Series contains the plots we want to show and how we want to show them. + Series []any `json:"series"` + // Events contains a list of 'events time series' names. Series with + // these names must be sent alongside other series. An event time series + // is just made of timestamps with no associated value, each of which + // gets plotted as a vertical line over another plot. + Events []string `json:"events"` + } + + Scatter struct { + Name string `json:"name"` + Title string `json:"title"` + Type string `json:"type"` + UpdateFreq int `json:"updateFreq"` + InfoText string `json:"infoText"` + Events string `json:"events"` + Layout ScatterLayout `json:"layout"` + Subplots []Subplot `json:"subplots"` + } + ScatterLayout struct { + Yaxis ScatterYAxis `json:"yaxis"` + BarMode string `json:"barmode"` + } + ScatterYAxis struct { + Title string `json:"title"` + TickSuffix string `json:"ticksuffix"` + } + + Subplot struct { + Name string `json:"name"` + Unitfmt string `json:"unitfmt"` + StackGroup string `json:"stackgroup"` + HoverOn string `json:"hoveron"` + Color string `json:"color"` + Type string `json:"type"` + } + + Heatmap struct { + Name string `json:"name"` + Title string `json:"title"` + Type string `json:"type"` + UpdateFreq int `json:"updateFreq"` + InfoText string `json:"infoText"` + Events string `json:"events"` + Layout HeatmapLayout `json:"layout"` + Colorscale []WeightedColor `json:"colorscale"` + Buckets []float64 `json:"buckets"` + CustomData []float64 `json:"custom_data"` + Hover HeapmapHover `json:"hover"` + } + HeatmapLayout struct { + YAxis HeatmapYaxis `json:"yaxis"` + } + HeatmapYaxis struct { + Title string `json:"title"` + TickMode string `json:"tickmode"` + TickVals []float64 `json:"tickvals"` + TickText []float64 `json:"ticktext"` + } + HeapmapHover struct { + YName string `json:"yname"` + YUnit string `json:"yunit"` // 'duration', 'bytes' or custom + ZName string `json:"zname"` + } +) diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/plot/plots.go b/image-scanner/vendor/github.com/arl/statsviz/internal/plot/plots.go new file mode 100644 index 000000000..68cfd824d --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/plot/plots.go @@ -0,0 +1,1320 @@ +package plot + +import ( + "math" + "runtime/metrics" + "time" +) + +func init() { + // lastgc and timestamp are both special cases. + + // lastgc draws vertical lines represeting GCs on certain plots. + registerRuntimePlot("lastgc") + // timestamp is the metric is the data for the time axis used on all plots. + registerRuntimePlot("timestamp") + + registerPlotFunc(makeHeapGlobalPlot) + registerPlotFunc(makeHeapDetailsPlot) + registerPlotFunc(makeLiveObjectsPlot) + registerPlotFunc(makeLiveBytesPlot) + registerPlotFunc(makeMSpanMCachePlot) + registerPlotFunc(makeMemoryClassesPlot) + registerPlotFunc(makeGoroutinesPlot) + registerPlotFunc(makeSizeClassesPlot) + registerPlotFunc(makeGCScanPlot) + registerPlotFunc(makeGCCyclesPlot) + registerPlotFunc(makeGCPausesPlot) + registerPlotFunc(makeCPUClassesGCPlot) + registerPlotFunc(makeRunnableTimePlot) + registerPlotFunc(makeMutexWaitPlot) + registerPlotFunc(makeGCStackSizePlot) + registerPlotFunc(makeSchedEventsPlot) + registerPlotFunc(makeCGOPlot) +} + +/* + * heap (global) + */ +var _ = registerRuntimePlot("heap-global", + "/memory/classes/heap/objects:bytes", + "/memory/classes/heap/unused:bytes", + "/memory/classes/heap/free:bytes", + "/memory/classes/heap/released:bytes", +) + +type heapGlobal struct { + enabled bool + + idxobj int + idxunused int + idxfree int + idxreleased int +} + +func makeHeapGlobalPlot(idxs map[string]int) runtimeMetric { + idxobj, ok1 := idxs["/memory/classes/heap/objects:bytes"] + idxunused, ok2 := idxs["/memory/classes/heap/unused:bytes"] + idxfree, ok3 := idxs["/memory/classes/heap/free:bytes"] + idxreleased, ok4 := idxs["/memory/classes/heap/released:bytes"] + + return &heapGlobal{ + enabled: ok1 && ok2 && ok3 && ok4, + idxobj: idxobj, + idxunused: idxunused, + idxfree: idxfree, + idxreleased: idxreleased, + } +} + +func (p *heapGlobal) name() string { return "heap-global" } +func (p *heapGlobal) isEnabled() bool { return p.enabled } + +func (p *heapGlobal) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Heap (global)", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "heap in-use", + Unitfmt: "%{y:.4s}B", + HoverOn: "points+fills", + StackGroup: "one", + }, + { + Name: "heap free", + Unitfmt: "%{y:.4s}B", + HoverOn: "points+fills", + StackGroup: "one", + }, + { + Name: "heap released", + Unitfmt: "%{y:.4s}B", + HoverOn: "points+fills", + StackGroup: "one", + }, + }, + InfoText: `Heap in use is /memory/classes/heap/objects + /memory/classes/heap/unused. It amounts to the memory occupied by live objects and dead objects that are not yet marked free by the GC, plus some memory reserved for heap objects. +Heap free is /memory/classes/heap/free, that is free memory that could be returned to the OS, but has not been. +Heap released is /memory/classes/heap/free, memory that is free memory that has been returned to the OS.`, + } + s.Layout.Yaxis.TickSuffix = "B" + s.Layout.Yaxis.Title = "bytes" + return s +} + +func (p *heapGlobal) values(samples []metrics.Sample) any { + heapObjects := samples[p.idxobj].Value.Uint64() + heapUnused := samples[p.idxunused].Value.Uint64() + + heapInUse := heapObjects + heapUnused + heapFree := samples[p.idxfree].Value.Uint64() + heapReleased := samples[p.idxreleased].Value.Uint64() + return []uint64{ + heapInUse, + heapFree, + heapReleased, + } +} + +/* + * heap (details) + */ +var _ = registerRuntimePlot("heap-details", + "/memory/classes/heap/objects:bytes", + "/memory/classes/heap/unused:bytes", + "/memory/classes/heap/free:bytes", + "/memory/classes/heap/released:bytes", + "/memory/classes/heap/stacks:bytes", + "/gc/heap/goal:bytes", +) + +type heapDetails struct { + enabled bool + + idxobj int + idxunused int + idxfree int + idxreleased int + idxstacks int + idxgoal int +} + +func makeHeapDetailsPlot(idxs map[string]int) runtimeMetric { + idxobj, ok1 := idxs["/memory/classes/heap/objects:bytes"] + idxunused, ok2 := idxs["/memory/classes/heap/unused:bytes"] + idxfree, ok3 := idxs["/memory/classes/heap/free:bytes"] + idxreleased, ok4 := idxs["/memory/classes/heap/released:bytes"] + idxstacks, ok5 := idxs["/memory/classes/heap/stacks:bytes"] + idxgoal, ok6 := idxs["/gc/heap/goal:bytes"] + + return &heapDetails{ + enabled: ok1 && ok2 && ok3 && ok4 && ok5 && ok6, + idxobj: idxobj, + idxunused: idxunused, + idxfree: idxfree, + idxreleased: idxreleased, + idxstacks: idxstacks, + idxgoal: idxgoal, + } +} + +func (p *heapDetails) name() string { return "heap-details" } +func (p *heapDetails) isEnabled() bool { return p.enabled } + +func (p *heapDetails) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Heap (details)", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "heap sys", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "heap objects", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "heap stacks", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "heap goal", + Unitfmt: "%{y:.4s}B", + }, + }, + InfoText: `Heap sys is /memory/classes/heap/objects + /memory/classes/heap/unused + /memory/classes/heap/released + /memory/classes/heap/free. It's an estimate of all the heap memory obtained form the OS. +Heap objects is /memory/classes/heap/objects, the memory occupied by live objects and dead objects that have not yet been marked free by the GC. +Heap stacks is /memory/classes/heap/stacks, the memory used for stack space. +Heap goal is gc/heap/goal, the heap size target for the end of the GC cycle.`, + } + s.Layout.Yaxis.TickSuffix = "B" + s.Layout.Yaxis.Title = "bytes" + return s +} + +func (p *heapDetails) values(samples []metrics.Sample) any { + heapObjects := samples[p.idxobj].Value.Uint64() + heapUnused := samples[p.idxunused].Value.Uint64() + + heapInUse := heapObjects + heapUnused + heapFree := samples[p.idxfree].Value.Uint64() + heapReleased := samples[p.idxreleased].Value.Uint64() + + heapIdle := heapReleased + heapFree + heapSys := heapInUse + heapIdle + heapStacks := samples[p.idxstacks].Value.Uint64() + nextGC := samples[p.idxgoal].Value.Uint64() + + return []uint64{ + heapSys, + heapObjects, + heapStacks, + nextGC, + } +} + +/* + * live objects + */ +var _ = registerRuntimePlot("live-objects", "/gc/heap/objects:objects") + +type liveObjects struct { + enabled bool + + idxobjects int +} + +func makeLiveObjectsPlot(idxs map[string]int) runtimeMetric { + idxobjects, ok := idxs["/gc/heap/objects:objects"] + + return &liveObjects{ + enabled: ok, + idxobjects: idxobjects, + } +} + +func (p *liveObjects) name() string { return "live-objects" } +func (p *liveObjects) isEnabled() bool { return p.enabled } + +func (p *liveObjects) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Live Objects in Heap", + Type: "bar", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "live objects", + Unitfmt: "%{y:.4s}", + Color: RGBString(255, 195, 128), + }, + }, + InfoText: `Live objects is /gc/heap/objects. It's the number of objects, live or unswept, occupying heap memory.`, + } + s.Layout.Yaxis.Title = "objects" + return s +} + +func (p *liveObjects) values(samples []metrics.Sample) any { + gcHeapObjects := samples[p.idxobjects].Value.Uint64() + return []uint64{ + gcHeapObjects, + } +} + +/* + * live bytes + */ +var _ = registerRuntimePlot("live-bytes", + "/gc/heap/allocs:bytes", + "/gc/heap/frees:bytes", +) + +type liveBytes struct { + enabled bool + + idxallocs int + idxfrees int +} + +func makeLiveBytesPlot(idxs map[string]int) runtimeMetric { + idxallocs, ok1 := idxs["/gc/heap/allocs:bytes"] + idxfrees, ok2 := idxs["/gc/heap/frees:bytes"] + + return &liveBytes{ + enabled: ok1 && ok2, + idxallocs: idxallocs, + idxfrees: idxfrees, + } +} + +func (p *liveBytes) name() string { return "live-bytes" } +func (p *liveBytes) isEnabled() bool { return p.enabled } + +func (p *liveBytes) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Live Bytes in Heap", + Type: "bar", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "live bytes", + Unitfmt: "%{y:.4s}B", + Color: RGBString(135, 182, 218), + }, + }, + InfoText: `Live bytes is /gc/heap/allocs - /gc/heap/frees. It's the number of bytes currently allocated (and not yet GC'ec) to the heap by the application.`, + } + s.Layout.Yaxis.Title = "bytes" + return s +} + +func (p *liveBytes) values(samples []metrics.Sample) any { + allocBytes := samples[p.idxallocs].Value.Uint64() + freedBytes := samples[p.idxfrees].Value.Uint64() + return []uint64{ + allocBytes - freedBytes, + } +} + +/* + * mspan mcache + */ +var _ = registerRuntimePlot("mspan-mcache", + "/memory/classes/metadata/mspan/inuse:bytes", + "/memory/classes/metadata/mspan/free:bytes", + "/memory/classes/metadata/mcache/inuse:bytes", + "/memory/classes/metadata/mcache/free:bytes", +) + +type mspanMcache struct { + enabled bool + + idxmspanInuse int + idxmspanFree int + idxmcacheInuse int + idxmcacheFree int +} + +func makeMSpanMCachePlot(idxs map[string]int) runtimeMetric { + idxmspanInuse, ok1 := idxs["/memory/classes/metadata/mspan/inuse:bytes"] + idxmspanFree, ok2 := idxs["/memory/classes/metadata/mspan/free:bytes"] + idxmcacheInuse, ok3 := idxs["/memory/classes/metadata/mcache/inuse:bytes"] + idxmcacheFree, ok4 := idxs["/memory/classes/metadata/mcache/free:bytes"] + + return &mspanMcache{ + enabled: ok1 && ok2 && ok3 && ok4, + idxmspanInuse: idxmspanInuse, + idxmspanFree: idxmspanFree, + idxmcacheInuse: idxmcacheInuse, + idxmcacheFree: idxmcacheFree, + } +} + +func (p *mspanMcache) name() string { return "mspan-mcache" } +func (p *mspanMcache) isEnabled() bool { return p.enabled } + +func (p *mspanMcache) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "MSpan/MCache", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "mspan in-use", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "mspan free", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "mcache in-use", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "mcache free", + Unitfmt: "%{y:.4s}B", + }, + }, + InfoText: `Mspan in-use is /memory/classes/metadata/mspan/inuse, the memory that is occupied by runtime mspan structures that are currently being used. +Mspan free is /memory/classes/metadata/mspan/free, the memory that is reserved for runtime mspan structures, but not in-use. +Mcache in-use is /memory/classes/metadata/mcache/inuse, the memory that is occupied by runtime mcache structures that are currently being used. +Mcache free is /memory/classes/metadata/mcache/free, the memory that is reserved for runtime mcache structures, but not in-use. +`, + } + s.Layout.Yaxis.Title = "bytes" + s.Layout.Yaxis.TickSuffix = "B" + return s +} + +func (p *mspanMcache) values(samples []metrics.Sample) any { + mspanInUse := samples[p.idxmspanInuse].Value.Uint64() + mspanSys := samples[p.idxmspanFree].Value.Uint64() + mcacheInUse := samples[p.idxmcacheInuse].Value.Uint64() + mcacheSys := samples[p.idxmcacheFree].Value.Uint64() + return []uint64{ + mspanInUse, + mspanSys, + mcacheInUse, + mcacheSys, + } +} + +/* + * goroutines + */ +var _ = registerRuntimePlot("goroutines", "/sched/goroutines:goroutines") + +type goroutines struct { + enabled bool + + idxgs int +} + +func makeGoroutinesPlot(idxs map[string]int) runtimeMetric { + idxgs, ok := idxs["/sched/goroutines:goroutines"] + + return &goroutines{ + enabled: ok, + idxgs: idxgs, + } +} + +func (p *goroutines) name() string { return "goroutines" } +func (p *goroutines) isEnabled() bool { return p.enabled } + +func (p *goroutines) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Goroutines", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "goroutines", + Unitfmt: "%{y}", + }, + }, + InfoText: "Goroutines is /sched/goroutines, the count of live goroutines.", + } + + s.Layout.Yaxis.Title = "goroutines" + return s +} + +func (p *goroutines) values(samples []metrics.Sample) any { + return []uint64{samples[p.idxgs].Value.Uint64()} +} + +/* + * size classes + */ +var _ = registerRuntimePlot("size-classes", + "/gc/heap/allocs-by-size:bytes", + "/gc/heap/frees-by-size:bytes", +) + +type sizeClasses struct { + enabled bool + sizeClasses []uint64 + + idxallocs int + idxfrees int +} + +func makeSizeClassesPlot(idxs map[string]int) runtimeMetric { + idxallocs, ok1 := idxs["/gc/heap/allocs-by-size:bytes"] + idxfrees, ok2 := idxs["/gc/heap/frees-by-size:bytes"] + + return &sizeClasses{ + enabled: ok1 && ok2, + idxallocs: idxallocs, + idxfrees: idxfrees, + } +} + +func (p *sizeClasses) name() string { return "size-classes" } +func (p *sizeClasses) isEnabled() bool { return p.enabled } + +func (p *sizeClasses) layout(samples []metrics.Sample) any { + // Perform a sanity check on the number of buckets on the 'allocs' and + // 'frees' size classes histograms. Statsviz plots a single histogram based + // on those 2 so we want them to have the same number of buckets, which + // should be true. + allocsBySize := samples[p.idxallocs].Value.Float64Histogram() + freesBySize := samples[p.idxfrees].Value.Float64Histogram() + if len(allocsBySize.Buckets) != len(freesBySize.Buckets) { + panic("different number of buckets in allocs and frees size classes histograms!") + } + + // Pre-allocate here so we never do it in values. + p.sizeClasses = make([]uint64, len(allocsBySize.Counts)) + + // No downsampling for the size classes histogram (factor=1) but we still + // need to adapt boundaries for plotly heatmaps. + buckets := downsampleBuckets(allocsBySize, 1) + + h := Heatmap{ + Name: p.name(), + Title: "Size Classes", + Type: "heatmap", + UpdateFreq: 5, + Colorscale: BlueShades, + Buckets: floatseq(len(buckets)), + CustomData: buckets, + Hover: HeapmapHover{ + YName: "size class", + YUnit: "bytes", + ZName: "objects", + }, + InfoText: `This heatmap shows the distribution of size classes, using /gc/heap/allocs-by-size and /gc/heap/frees-by-size.`, + Layout: HeatmapLayout{ + YAxis: HeatmapYaxis{ + Title: "size class", + TickMode: "array", + TickVals: []float64{1, 9, 17, 25, 31, 37, 43, 50, 58, 66}, + TickText: []float64{1 << 4, 1 << 7, 1 << 8, 1 << 9, 1 << 10, 1 << 11, 1 << 12, 1 << 13, 1 << 14, 1 << 15}, + }, + }, + } + return h +} + +func (p *sizeClasses) values(samples []metrics.Sample) any { + allocsBySize := samples[p.idxallocs].Value.Float64Histogram() + freesBySize := samples[p.idxfrees].Value.Float64Histogram() + + for i := 0; i < len(p.sizeClasses); i++ { + p.sizeClasses[i] = allocsBySize.Counts[i] - freesBySize.Counts[i] + } + return p.sizeClasses +} + +/* + * gc pauses + */ +var _ = registerRuntimePlot("gc-pauses", "/gc/pauses:seconds") + +type gcpauses struct { + enabled bool + histfactor int + counts [maxBuckets]uint64 + + idxgcpauses int +} + +func makeGCPausesPlot(idxs map[string]int) runtimeMetric { + idxgcpauses, ok := idxs["/gc/pauses:seconds"] + + return &gcpauses{ + enabled: ok, + idxgcpauses: idxgcpauses, + } +} + +func (p *gcpauses) name() string { return "gc-pauses" } +func (p *gcpauses) isEnabled() bool { return p.enabled } + +func (p *gcpauses) layout(samples []metrics.Sample) any { + gcpauses := samples[p.idxgcpauses].Value.Float64Histogram() + p.histfactor = downsampleFactor(len(gcpauses.Buckets), maxBuckets) + buckets := downsampleBuckets(gcpauses, p.histfactor) + + h := Heatmap{ + Name: p.name(), + Title: "Stop-the-world Pause Latencies", + Type: "heatmap", + UpdateFreq: 5, + Colorscale: PinkShades, + Buckets: floatseq(len(buckets)), + CustomData: buckets, + Hover: HeapmapHover{ + YName: "pause duration", + YUnit: "duration", + ZName: "pauses", + }, + Layout: HeatmapLayout{ + YAxis: HeatmapYaxis{ + Title: "pause duration", + TickMode: "array", + TickVals: []float64{6, 13, 20, 26, 33, 39.5, 46, 53, 60, 66, 73, 79, 86}, + TickText: []float64{1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 5e-3, 1e-2, 5e-2, 1e-1, 5e-1, 1, 5, 10}, + }, + }, + InfoText: `This heatmap shows the distribution of individual GC-related stop-the-world pause latencies, uses /gc/pauses:seconds,.`, + } + return h +} + +func (p *gcpauses) values(samples []metrics.Sample) any { + gcpauses := samples[p.idxgcpauses].Value.Float64Histogram() + return downsampleCounts(gcpauses, p.histfactor, p.counts[:]) +} + +/* + * time spent in runnable state + */ +var _ = registerRuntimePlot("runnable-time", "/sched/latencies:seconds") + +type runnableTime struct { + enabled bool + histfactor int + counts [maxBuckets]uint64 + + idxschedlat int +} + +func makeRunnableTimePlot(idxs map[string]int) runtimeMetric { + idxschedlat, ok := idxs["/sched/latencies:seconds"] + + return &runnableTime{ + enabled: ok, + idxschedlat: idxschedlat, + } +} + +func (p *runnableTime) name() string { return "runnable-time" } +func (p *runnableTime) isEnabled() bool { return p.enabled } + +func (p *runnableTime) layout(samples []metrics.Sample) any { + schedlat := samples[p.idxschedlat].Value.Float64Histogram() + p.histfactor = downsampleFactor(len(schedlat.Buckets), maxBuckets) + buckets := downsampleBuckets(schedlat, p.histfactor) + + h := Heatmap{ + Name: p.name(), + Title: "Time Goroutines Spend in 'Runnable' state", + Type: "heatmap", + UpdateFreq: 5, + Colorscale: GreenShades, + Buckets: floatseq(len(buckets)), + CustomData: buckets, + Hover: HeapmapHover{ + YName: "duration", + YUnit: "duration", + ZName: "goroutines", + }, + Layout: HeatmapLayout{ + YAxis: HeatmapYaxis{ + Title: "duration", + TickMode: "array", + TickVals: []float64{6, 13, 20, 26, 33, 39.5, 46, 53, 60, 66, 73, 79, 86}, + TickText: []float64{1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 5e-3, 1e-2, 5e-2, 1e-1, 5e-1, 1, 5, 10}, + }, + }, + InfoText: `This heatmap shows the distribution of the time goroutines have spent in the scheduler in a runnable state before actually running, uses /sched/latencies:seconds.`, + } + + return h +} + +func (p *runnableTime) values(samples []metrics.Sample) any { + schedlat := samples[p.idxschedlat].Value.Float64Histogram() + + return downsampleCounts(schedlat, p.histfactor, p.counts[:]) +} + +/* + * scheduling events + */ +var _ = registerRuntimePlot("sched-events", + "/sched/latencies:seconds", + "/sched/gomaxprocs:threads", +) + +type schedEvents struct { + enabled bool + + idxschedlat int + idxGomaxprocs int + lasttot uint64 +} + +func makeSchedEventsPlot(idxs map[string]int) runtimeMetric { + idxschedlat, ok1 := idxs["/sched/latencies:seconds"] + idxGomaxprocs, ok2 := idxs["/sched/gomaxprocs:threads"] + + return &schedEvents{ + enabled: ok1 && ok2, + idxschedlat: idxschedlat, + idxGomaxprocs: idxGomaxprocs, + lasttot: math.MaxUint64, + } +} + +func (p *schedEvents) name() string { return "sched-events" } +func (p *schedEvents) isEnabled() bool { return p.enabled } + +func (p *schedEvents) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Goroutine Scheduling Events", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "events per unit of time", + Unitfmt: "%{y}", + }, + { + Name: "events per unit of time, per P", + Unitfmt: "%{y}", + }, + }, + InfoText: `Events per second is the sum of all buckets in /sched/latencies:seconds, that is, it tracks the total number of goroutine scheduling events. That number is multiplied by the constant 8. +Events per second per P (processor) is Events per second divided by current GOMAXPROCS, from /sched/gomaxprocs:threads. +NOTE: the multiplying factor comes from internal Go runtime source code and might change from version to version.`, + } + s.Layout.Yaxis.Title = "events" + return s +} + +// gTrackingPeriod is currently always 8. Guard it behind build tags when that +// changes. See https://github.com/golang/go/blob/go1.18.4/src/runtime/runtime2.go#L502-L504 +const currentGtrackingPeriod = 8 + +// TODO show scheduling events per seconds +func (p *schedEvents) values(samples []metrics.Sample) any { + schedlat := samples[p.idxschedlat].Value.Float64Histogram() + gomaxprocs := samples[p.idxGomaxprocs].Value.Uint64() + + total := uint64(0) + for _, v := range schedlat.Counts { + total += v + } + total *= currentGtrackingPeriod + + curtot := total - p.lasttot + if p.lasttot == math.MaxUint64 { + // We don't want a big spike at statsviz launch in case the process has + // been running for some time and curtot is high. + curtot = 0 + } + p.lasttot = total + + ftot := float64(curtot) + + return []float64{ + ftot, + ftot / float64(gomaxprocs), + } +} + +/* + * cgo + */ +var _ = registerRuntimePlot("cgo", "/cgo/go-to-c-calls:calls") + +type cgo struct { + enabled bool + idxgo2c int + lastgo2c uint64 +} + +func makeCGOPlot(idxs map[string]int) runtimeMetric { + idxgo2c, ok := idxs["/cgo/go-to-c-calls:calls"] + + return &cgo{ + enabled: ok, + idxgo2c: idxgo2c, + lastgo2c: math.MaxUint64, + } +} + +func (p *cgo) name() string { return "cgo" } +func (p *cgo) isEnabled() bool { return p.enabled } + +func (p *cgo) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "CGO Calls", + Type: "bar", + Subplots: []Subplot{ + { + Name: "calls from go to c", + Unitfmt: "%{y}", + Color: "red", + }, + }, + InfoText: "Shows the count of calls made from Go to C by the current process, per unit of time. Uses /cgo/go-to-c-calls:calls", + } + + s.Layout.Yaxis.Title = "calls" + return s +} + +// TODO show cgo calls per second +func (p *cgo) values(samples []metrics.Sample) any { + go2c := samples[p.idxgo2c].Value.Uint64() + curgo2c := go2c - p.lastgo2c + if p.lastgo2c == math.MaxUint64 { + curgo2c = 0 + } + p.lastgo2c = go2c + + return []uint64{curgo2c} +} + +/* + * gc stack size + */ +var _ = registerRuntimePlot("gc-stack-size", "/gc/stack/starting-size:bytes") + +type gcStackSize struct { + enabled bool + idxstack int +} + +func makeGCStackSizePlot(idxs map[string]int) runtimeMetric { + idxstack, ok := idxs["/gc/stack/starting-size:bytes"] + + return &gcStackSize{ + enabled: ok, + idxstack: idxstack, + } +} + +func (p *gcStackSize) name() string { return "gc-stack-size" } +func (p *gcStackSize) isEnabled() bool { return p.enabled } + +func (p *gcStackSize) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Starting Size of Goroutines Stacks", + Type: "scatter", + Subplots: []Subplot{ + { + Name: "new goroutines stack size", + Unitfmt: "%{y:.4s}B", + }, + }, + InfoText: "Shows the stack size of new goroutines, uses /gc/stack/starting-size:bytes", + } + + s.Layout.Yaxis.Title = "bytes" + return s +} + +func (p *gcStackSize) values(samples []metrics.Sample) any { + stackSize := samples[p.idxstack].Value.Uint64() + return []uint64{stackSize} +} + +/* + * GC cycles + */ +var _ = registerRuntimePlot("gc-cycles", + "/gc/cycles/automatic:gc-cycles", + "/gc/cycles/forced:gc-cycles", + "/gc/cycles/total:gc-cycles", +) + +type gcCycles struct { + enabled bool + + idxAutomatic int + idxForced int + idxTotal int + + lastAuto, lastForced, lastTotal uint64 +} + +func makeGCCyclesPlot(idxs map[string]int) runtimeMetric { + idxAutomatic, ok1 := idxs["/gc/cycles/automatic:gc-cycles"] + idxForced, ok2 := idxs["/gc/cycles/forced:gc-cycles"] + idxTotal, ok3 := idxs["/gc/cycles/total:gc-cycles"] + + return &gcCycles{ + enabled: ok1 && ok2 && ok3, + idxAutomatic: idxAutomatic, + idxForced: idxForced, + idxTotal: idxTotal, + } +} + +func (p *gcCycles) name() string { return "gc-cycles" } +func (p *gcCycles) isEnabled() bool { return p.enabled } + +func (p *gcCycles) layout(_ []metrics.Sample) any { + return Scatter{ + Name: p.name(), + Title: "Completed GC Cycles", + Type: "bar", + Subplots: []Subplot{ + { + Name: "automatic", + Unitfmt: "%{y}", + Type: "bar", + }, + { + Name: "forced", + Unitfmt: "%{y}", + Type: "bar", + }, + }, + InfoText: `Number of completed GC cycles, either forced of generated by the Go runtime.`, + Layout: ScatterLayout{ + BarMode: "stack", + Yaxis: ScatterYAxis{ + Title: "cycles", + }, + }, + } +} + +func (p *gcCycles) values(samples []metrics.Sample) any { + total := samples[p.idxTotal].Value.Uint64() + auto := samples[p.idxAutomatic].Value.Uint64() + forced := samples[p.idxForced].Value.Uint64() + + if p.lastTotal == 0 { + p.lastTotal = total + p.lastForced = forced + p.lastAuto = auto + return []uint64{0, 0} + } + + ret := []uint64{ + auto - p.lastAuto, + forced - p.lastForced, + } + + p.lastForced = forced + p.lastAuto = auto + + return ret +} + +/* +* mspan mcache + */ +var _ = registerRuntimePlot("memory-classes", + "/memory/classes/os-stacks:bytes", + "/memory/classes/other:bytes", + "/memory/classes/profiling/buckets:bytes", + "/memory/classes/total:bytes", +) + +type memoryClasses struct { + enabled bool + + idxOSStacks int + idxOther int + idxProfBuckets int + idxTotal int +} + +func makeMemoryClassesPlot(idxs map[string]int) runtimeMetric { + idxOSStacks, ok1 := idxs["/memory/classes/os-stacks:bytes"] + idxOther, ok2 := idxs["/memory/classes/other:bytes"] + idxProfBuckets, ok3 := idxs["/memory/classes/profiling/buckets:bytes"] + idxTotal, ok4 := idxs["/memory/classes/total:bytes"] + + return &memoryClasses{ + enabled: ok1 && ok2 && ok3 && ok4, + idxOSStacks: idxOSStacks, + idxOther: idxOther, + idxProfBuckets: idxProfBuckets, + idxTotal: idxTotal, + } +} + +func (p *memoryClasses) name() string { return "memory-classes" } +func (p *memoryClasses) isEnabled() bool { return p.enabled } + +func (p *memoryClasses) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Memory classes", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "os stacks", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "other", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "profiling buckets", + Unitfmt: "%{y:.4s}B", + }, + { + Name: "total", + Unitfmt: "%{y:.4s}B", + }, + }, + + InfoText: `OS stacks is /memory/classes/os-stacks, stack memory allocated by the underlying operating system. +Other is /memory/classes/other, memory used by execution trace buffers, structures for debugging the runtime, finalizer and profiler specials, and more. +Profiling buckets is /memory/classes/profiling/buckets, memory that is used by the stack trace hash map used for profiling. +Total is /memory/classes/total, all memory mapped by the Go runtime into the current process as read-write.`, + } + s.Layout.Yaxis.Title = "bytes" + s.Layout.Yaxis.TickSuffix = "B" + return s +} + +func (p *memoryClasses) values(samples []metrics.Sample) any { + osStacks := samples[p.idxOSStacks].Value.Uint64() + other := samples[p.idxOther].Value.Uint64() + profBuckets := samples[p.idxProfBuckets].Value.Uint64() + total := samples[p.idxTotal].Value.Uint64() + + return []uint64{ + osStacks, + other, + profBuckets, + total, + } +} + +/* +* cpu classes (gc) + */ +var _ = registerRuntimePlot("cpu-classes-gc", + "/cpu/classes/gc/mark/assist:cpu-seconds", + "/cpu/classes/gc/mark/dedicated:cpu-seconds", + "/cpu/classes/gc/mark/idle:cpu-seconds", + "/cpu/classes/gc/pause:cpu-seconds", + "/cpu/classes/gc/total:cpu-seconds", +) + +type cpuClassesGC struct { + enabled bool + + idxMarkAssist int + idxMarkDedicated int + idxMarkIdle int + idxPause int + idxTotal int + + lastTime time.Time + + lastMarkAssist float64 + lastMarkDedicated float64 + lastMarkIdle float64 + lastPause float64 + lastTotal float64 +} + +func makeCPUClassesGCPlot(idxs map[string]int) runtimeMetric { + idxMarkAssist, ok1 := idxs["/cpu/classes/gc/mark/assist:cpu-seconds"] + idxMarkDedicated, ok2 := idxs["/cpu/classes/gc/mark/dedicated:cpu-seconds"] + idxMarkIdle, ok3 := idxs["/cpu/classes/gc/mark/idle:cpu-seconds"] + idxPause, ok4 := idxs["/cpu/classes/gc/pause:cpu-seconds"] + idxTotal, ok5 := idxs["/cpu/classes/gc/total:cpu-seconds"] + + return &cpuClassesGC{ + enabled: ok1 && ok2 && ok3 && ok4 && ok5, + idxMarkAssist: idxMarkAssist, + idxMarkDedicated: idxMarkDedicated, + idxMarkIdle: idxMarkIdle, + idxPause: idxPause, + idxTotal: idxTotal, + } +} + +func (p *cpuClassesGC) name() string { return "cpu-classes-gc" } +func (p *cpuClassesGC) isEnabled() bool { return p.enabled } + +func (p *cpuClassesGC) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "CPU classes (GC)", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "mark assist", + Unitfmt: "%{y:.4s}s", + }, + { + Name: "mark dedicated", + Unitfmt: "%{y:.4s}s", + }, + { + Name: "mark idle", + Unitfmt: "%{y:.4s}s", + }, + { + Name: "pause", + Unitfmt: "%{y:.4s}s", + }, + { + Name: "total", + Unitfmt: "%{y:.4s}s", + }, + }, + + InfoText: `Cumulative metrics are converted to rates by Statsviz so as to be more easily comparable and readable. +All this metrics are overestimates, and not directly comparable to system CPU time measurements. Compare only with other /cpu/classes metrics. + +mark assist is /cpu/classes/gc/mark/assist, estimated total CPU time goroutines spent performing GC tasks to assist the GC and prevent it from falling behind the application. +mark dedicated is /cpu/classes/gc/mark/dedicated, Estimated total CPU time spent performing GC tasks on processors (as defined by GOMAXPROCS) dedicated to those tasks. +mark idle is /cpu/classes/gc/mark/idle, estimated total CPU time spent performing GC tasks on spare CPU resources that the Go scheduler could not otherwise find a use for. +pause is /cpu/classes/gc/pause, estimated total CPU time spent with the application paused by the GC. +total is /cpu/classes/gc/total, estimated total CPU time spent performing GC tasks.`, + } + s.Layout.Yaxis.Title = "cpu-seconds per seconds" + s.Layout.Yaxis.TickSuffix = "s" + return s +} + +func (p *cpuClassesGC) values(samples []metrics.Sample) any { + curMarkAssist := samples[p.idxMarkAssist].Value.Float64() + curMarkDedicated := samples[p.idxMarkDedicated].Value.Float64() + curMarkIdle := samples[p.idxMarkIdle].Value.Float64() + curPause := samples[p.idxPause].Value.Float64() + curTotal := samples[p.idxTotal].Value.Float64() + + if p.lastTime.IsZero() { + p.lastMarkAssist = curMarkAssist + p.lastMarkDedicated = curMarkDedicated + p.lastMarkIdle = curMarkIdle + p.lastPause = curPause + p.lastTotal = curTotal + p.lastTime = time.Now() + + return []float64{0, 0, 0, 0, 0} + } + + t := time.Since(p.lastTime).Seconds() + + markAssist := (curMarkAssist - p.lastMarkAssist) / t + markDedicated := (curMarkDedicated - p.lastMarkDedicated) / t + markIdle := (curMarkIdle - p.lastMarkIdle) / t + pause := (curPause - p.lastPause) / t + total := (curTotal - p.lastTotal) / t + + p.lastMarkAssist = curMarkAssist + p.lastMarkDedicated = curMarkDedicated + p.lastMarkIdle = curMarkIdle + p.lastPause = curPause + p.lastTotal = curTotal + p.lastTime = time.Now() + + return []float64{ + markAssist, + markDedicated, + markIdle, + pause, + total, + } +} + +/* +* mutex wait + */ +var _ = registerRuntimePlot("mutex-wait", + "/sync/mutex/wait/total:seconds", +) + +type mutexWait struct { + enabled bool + idxMutexWait int + + lastTime time.Time + lastMutexWait float64 +} + +func makeMutexWaitPlot(idxs map[string]int) runtimeMetric { + idxMutexWait, ok := idxs["/cpu/classes/gc/mark/assist:cpu-seconds"] + + return &mutexWait{ + enabled: ok, + idxMutexWait: idxMutexWait, + } +} + +func (p *mutexWait) name() string { return "mutex-wait" } +func (p *mutexWait) isEnabled() bool { return p.enabled } + +func (p *mutexWait) layout(_ []metrics.Sample) any { + s := Scatter{ + Name: p.name(), + Title: "Time Goroutines Spend Blocked on Mutexes", + Type: "scatter", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "mutex wait", + Unitfmt: "%{y:.4s}s", + }, + }, + + InfoText: `Cumulative metrics are converted to rates by Statsviz so as to be more easily comparable and readable. +mutex wait is /sync/mutex/wait/total, approximate cumulative time goroutines have spent blocked on a sync.Mutex or sync.RWMutex. + +This metric is useful for identifying global changes in lock contention. Collect a mutex or block profile using the runtime/pprof package for more detailed contention data.`, + } + s.Layout.Yaxis.Title = "seconds per seconds" + s.Layout.Yaxis.TickSuffix = "s" + return s +} + +func (p *mutexWait) values(samples []metrics.Sample) any { + if p.lastTime.IsZero() { + p.lastTime = time.Now() + p.lastMutexWait = samples[p.idxMutexWait].Value.Float64() + + return []float64{0} + } + + t := time.Since(p.lastTime).Seconds() + + mutexWait := (samples[p.idxMutexWait].Value.Float64() - p.lastMutexWait) / t + + p.lastMutexWait = samples[p.idxMutexWait].Value.Float64() + p.lastTime = time.Now() + + return []float64{ + mutexWait, + } +} + +/* + * gc scan + */ +var _ = registerRuntimePlot("gc-scan", + "/gc/scan/globals:bytes", + "/gc/scan/heap:bytes", + "/gc/scan/stack:bytes", + "/gc/scan/total:bytes", +) + +type gcScan struct { + enabled bool + + idxGlobals int + idxHeap int + idxStack int +} + +func makeGCScanPlot(idxs map[string]int) runtimeMetric { + idxGlobals, ok1 := idxs["/gc/scan/globals:bytes"] + idxHeap, ok2 := idxs["/gc/scan/heap:bytes"] + idxStack, ok3 := idxs["/gc/scan/stack:bytes"] + + return &gcScan{ + enabled: ok1 && ok2 && ok3, + idxGlobals: idxGlobals, + idxHeap: idxHeap, + idxStack: idxStack, + } +} + +func (p *gcScan) name() string { return "gc-scan" } +func (p *gcScan) isEnabled() bool { return p.enabled } + +func (p *gcScan) layout(_ []metrics.Sample) any { + return Scatter{ + Name: p.name(), + Title: "GC Scan", + Type: "bar", + Events: "lastgc", + Subplots: []Subplot{ + { + Name: "scannable globals", + Unitfmt: "%{y:.4s}B", + Type: "bar", + }, + { + Name: "scannable heap", + Unitfmt: "%{y:.4s}B", + Type: "bar", + }, + { + Name: "scanned stack", + Unitfmt: "%{y:.4s}B", + Type: "bar", + }, + }, + InfoText: `This plot shows the amount of memory that is scannable by the GC. +scannable globals is /gc/scan/globals, the total amount of global variable space that is scannable. +scannable heap is /gc/scan/heap, the total amount of heap space that is scannable. +scanned stack is /gc/scan/stack, the number of bytes of stack that were scanned last GC cycle. +`, + Layout: ScatterLayout{ + BarMode: "stack", + Yaxis: ScatterYAxis{ + TickSuffix: "B", + Title: "bytes", + }, + }, + } +} + +func (p *gcScan) values(samples []metrics.Sample) any { + globals := samples[p.idxGlobals].Value.Uint64() + heap := samples[p.idxHeap].Value.Uint64() + stack := samples[p.idxStack].Value.Uint64() + return []uint64{ + globals, + heap, + stack, + } +} + +/* + * helpers + */ + +func floatseq(n int) []float64 { + seq := make([]float64, n) + for i := 0; i < n; i++ { + seq[i] = float64(i) + } + return seq +} diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/plot/plots_list.go b/image-scanner/vendor/github.com/arl/statsviz/internal/plot/plots_list.go new file mode 100644 index 000000000..a25342094 --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/plot/plots_list.go @@ -0,0 +1,162 @@ +package plot + +import ( + "encoding/json" + "fmt" + "io" + "runtime/debug" + "runtime/metrics" + "sync" + "time" +) + +var ( + names map[string]bool + usedMetrics map[string]struct{} +) + +type plotFunc func(idxs map[string]int) runtimeMetric + +var plotFuncs []plotFunc + +func registerPlotFunc(f plotFunc) { + plotFuncs = append(plotFuncs, f) +} + +func registerRuntimePlot(name string, metrics ...string) bool { + if names == nil { + names = make(map[string]bool) + usedMetrics = make(map[string]struct{}) + } + if names[name] { + panic(name + " is an already reserved plot name") + } + names[name] = true + + // Record the metrics we use. + for _, m := range metrics { + usedMetrics[m] = struct{}{} + } + + return true +} + +func IsReservedPlotName(name string) bool { + return names[name] +} + +type runtimeMetric interface { + name() string + isEnabled() bool + layout([]metrics.Sample) any + values([]metrics.Sample) any +} + +// List holds all the plots that statsviz knows about. Some plots might be +// disabled, if they rely on metrics that are unknown to the current Go version. +type List struct { + rtPlots []runtimeMetric + userPlots []UserPlot + + once sync.Once // ensure Config is called once + cfg *Config + + idxs map[string]int // map metrics name to idx in samples and descs + descs []metrics.Description + + mu sync.Mutex // protects samples in case of concurrent calls to WriteValues + samples []metrics.Sample +} + +func NewList(userPlots []UserPlot) (*List, error) { + if name := hasDuplicatePlotNames(userPlots); name != "" { + return nil, fmt.Errorf("duplicate plot name %s", name) + } + + descs := metrics.All() + pl := &List{ + idxs: make(map[string]int), + descs: descs, + samples: make([]metrics.Sample, len(descs)), + userPlots: userPlots, + } + for i := range pl.samples { + pl.samples[i].Name = pl.descs[i].Name + pl.idxs[pl.samples[i].Name] = i + } + metrics.Read(pl.samples) + + return pl, nil +} + +func (pl *List) Config() *Config { + pl.once.Do(func() { + pl.rtPlots = make([]runtimeMetric, 0, len(plotFuncs)) + for _, f := range plotFuncs { + pl.rtPlots = append(pl.rtPlots, f(pl.idxs)) + } + + layouts := make([]any, 0, len(pl.rtPlots)) + for i := range pl.rtPlots { + if pl.rtPlots[i].isEnabled() { + layouts = append(layouts, pl.rtPlots[i].layout(pl.samples)) + } + } + + pl.cfg = &Config{ + Events: []string{"lastgc"}, + Series: layouts, + } + + // User plots go at the back of the list for now. + for i := range pl.userPlots { + pl.cfg.Series = append(pl.cfg.Series, pl.userPlots[i].Layout()) + } + }) + return pl.cfg +} + +// WriteValues writes into w a JSON object containing the data points for all +// plots at the current instant. +func (pl *List) WriteValues(w io.Writer) error { + pl.mu.Lock() + defer pl.mu.Unlock() + + metrics.Read(pl.samples) + + // lastgc time series is used as source to represent garbage collection + // timestamps as vertical bars on certain plots. + gcStats := debug.GCStats{} + debug.ReadGCStats(&gcStats) + + m := map[string]any{ + // javascript timestampts are in milliseconds + "lastgc": []int64{gcStats.LastGC.UnixMilli()}, + "timestamp": time.Now().UnixMilli(), + } + + for _, p := range pl.rtPlots { + if p.isEnabled() { + m[p.name()] = p.values(pl.samples) + } + } + + for i := range pl.userPlots { + up := &pl.userPlots[i] + switch { + case up.Scatter != nil: + vals := make([]float64, len(up.Scatter.Funcs)) + for i := range up.Scatter.Funcs { + vals[i] = up.Scatter.Funcs[i]() + } + m[up.Scatter.Plot.Name] = vals + case up.Heatmap != nil: + panic("unimplemented") + } + } + + if err := json.NewEncoder(w).Encode(m); err != nil { + return fmt.Errorf("failed to write/convert metrics values to json: %v", err) + } + return nil +} diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/plot/user.go b/image-scanner/vendor/github.com/arl/statsviz/internal/plot/user.go new file mode 100644 index 000000000..08d13b3f0 --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/plot/user.go @@ -0,0 +1,48 @@ +package plot + +type ScatterUserPlot struct { + Plot Scatter + Funcs []func() float64 +} + +type HeatmapUserPlot struct { + Plot Heatmap + // TODO(arl): heatmap get value func +} + +type UserPlot struct { + Scatter *ScatterUserPlot + Heatmap *HeatmapUserPlot +} + +func (up UserPlot) Layout() any { + switch { + case (up.Scatter != nil) == (up.Heatmap != nil): + panic("userplot must be a timeseries or a heatmap") + case up.Scatter != nil: + return up.Scatter.Plot + case up.Heatmap != nil: + return up.Heatmap.Plot + } + + panic("unreachable") +} + +func hasDuplicatePlotNames(userPlots []UserPlot) string { + names := map[string]bool{} + for _, p := range userPlots { + name := "" + if p.Scatter != nil { + name = p.Scatter.Plot.Name + } else if p.Heatmap != nil { + name = p.Heatmap.Plot.Name + } else { + panic("both heapmap and scatter are nil") + } + if names[name] { + return name + } + names[name] = true + } + return "" +} diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/static/css/statsviz.css b/image-scanner/vendor/github.com/arl/statsviz/internal/static/css/statsviz.css new file mode 100644 index 000000000..d9c6e2ab4 --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/static/css/statsviz.css @@ -0,0 +1,62 @@ +html, +body { + height: 100%; + margin: 0; +} + +nav.navbar { + background-color: #f8f9fa; +} + +body.dark-theme { + color: white; + background-color: #282a2c; +} + +body.dark-theme nav.navbar span.action svg { + color: lightgray; +} + +#header { + text-align: center; +} + +.container-xxl { + width: 2190px; +} + +.tooltip-table { + display: table; + width: 160px; + border-collapse: collapse; +} + +.tooltip-style { + font-family: "Open Sans", verdana, arial, sans-serif; + font-size: 12px; + fill: rgb(68, 68, 68); + fill-opacity: 1; + white-space: pre; +} + +.tooltip-row { + display: table-row; +} + +.tooltip-value, +.tooltip-label { + display: table-cell; + padding: 0.3em; + /* border: #f0f0f0 1px solid; */ +} + +.tooltip-label { + font-weight: bold; +} + +.plots-wrapper { + display: grid; + height: 100%; + /* column must be same as plotWidth (plot.js) */ + grid-template-columns: repeat(auto-fill, minmax(0px, 630px)); +} \ No newline at end of file diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/static/fs.go b/image-scanner/vendor/github.com/arl/statsviz/internal/static/fs.go new file mode 100644 index 000000000..835ca00f2 --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/static/fs.go @@ -0,0 +1,6 @@ +package static + +import "embed" + +//go:embed * +var Assets embed.FS diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/static/index.html b/image-scanner/vendor/github.com/arl/statsviz/internal/static/index.html new file mode 100644 index 000000000..f032b78ce --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/static/index.html @@ -0,0 +1,61 @@ + + + + + Statsviz + + + + + + + + + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/app.js b/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/app.js new file mode 100644 index 000000000..72ba5b2c3 --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/app.js @@ -0,0 +1,145 @@ +import * as stats from './stats.js'; +import * as plot from "./plot.js"; +import * as theme from "./theme.js"; +import PlotsDef from './plotsdef.js'; + +const buildWebsocketURI = () => { + var loc = window.location, + ws_prot = "ws:"; + if (loc.protocol === "https:") { + ws_prot = "wss:"; + } + return ws_prot + "//" + loc.host + loc.pathname + "ws" +} + +const dataRetentionSeconds = 600; +var timeout = 250; + +const clamp = (val, min, max) => { + if (val < min) return min; + if (val > max) return max; + return val; +} + +/* nav bar ui management */ +let paused = false; +let show_gc = true; +let timerange = 60; + +/* WebSocket connection handling */ +const connect = () => { + const uri = buildWebsocketURI(); + let ws = new WebSocket(uri); + console.info(`Attempting websocket connection to server at ${uri}`); + + ws.onopen = () => { + console.info("Successfully connected"); + timeout = 250; // reset connection timeout for next time + }; + + ws.onclose = event => { + console.error(`Closed websocket connection: code ${event.code}`); + setTimeout(connect, clamp(timeout += timeout, 250, 5000)); + }; + + ws.onerror = err => { + console.error(`Websocket error, closing connection.`); + ws.close(); + }; + + let initDone = false; + ws.onmessage = event => { + let data = JSON.parse(event.data) + + if (!initDone) { + configurePlots(PlotsDef); + stats.init(PlotsDef, dataRetentionSeconds); + + attachPlots(); + + $('#play_pause').change(() => { paused = !paused; }); + $('#show_gc').change(() => { + show_gc = !show_gc; + updatePlots(); + }); + $('#select_timerange').click(() => { + const val = parseInt($("#select_timerange option:selected").val(), 10); + timerange = val; + updatePlots(); + }); + initDone = true; + return; + } + + stats.pushData(data); + if (paused) { + return + } + updatePlots(PlotsDef.events); + } +} + +connect(); + +let plots = []; + +const configurePlots = (plotdefs) => { + plots = []; + plotdefs.series.forEach(plotdef => { + plots.push(new plot.Plot(plotdef)); + }); +} + +const attachPlots = () => { + let plotsDiv = $('#plots'); + plotsDiv.empty(); + + for (let i = 0; i < plots.length; i++) { + const plot = plots[i]; + let div = $(`
`); + plot.createElement(div[0], i) + plotsDiv.append(div); + } +} + +const updatePlots = () => { + // Create shapes. + let shapes = new Map(); + + let data = stats.slice(timerange); + + if (show_gc) { + for (const [name, serie] of data.events) { + shapes.set(name, plot.createVerticalLines(serie)); + } + } + + // Always show the full range on x axis. + const now = data.times[data.times.length - 1]; + let xrange = [now - timerange * 1000, now]; + + plots.forEach(plot => { + if (!plot.hidden) { + plot.update(xrange, data, shapes); + } + }); +} + +const updatePlotsLayout = () => { + plots.forEach(plot => { + plot.updateTheme(); + }); +} + +theme.updateThemeMode(); + +/** + * Change color theme when the user presses the theme switch button + */ +$('#color_theme_sw').change(() => { + const themeMode = theme.getThemeMode(); + const newTheme = themeMode === "dark" && "light" || "dark"; + localStorage.setItem("theme-mode", newTheme); + theme.updateThemeMode(); + updatePlotsLayout(); +}); diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/buffer.js b/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/buffer.js new file mode 100644 index 000000000..7b205aeaa --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/buffer.js @@ -0,0 +1,46 @@ +// Buffer declares the Buffer class. +export default class Buffer { + constructor(len, cap) { + if (cap - len < 0) { + throw "cap - len must be positive"; + } + // TODO(arl): value using TypedArray rather than Array here + this._buf = new Array(cap); + this._pos = 0; + this._len = len; + this._cap = cap; + } + last() { + if (this.length() == 0) { + throw 'Cannot call last() on an empty Buffer'; + } + return this._buf[this._pos]; + } + push(pt) { + if (this._pos >= this._cap) { + // move data to the beginning of the buffer, effectively discarding + // the cap-len oldest elements + this._buf.copyWithin(0, this._cap - this._len); + this._pos = this._len; + } + this._buf[this._pos] = pt; + this._pos++; + } + length() { + if (this._pos > this._len) { + return this._len; + } + return this._pos; + } + + // slice returns a slice of the len latest datapoints present in the buffer. + slice(len) { + // Cap the dimension of the returned slice to the data available + if (len > this.length()) { + len = this.length(); + } + + let start = this._pos - len; + return this._buf.slice(start, start + len); + } +}; diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/plot.js b/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/plot.js new file mode 100644 index 000000000..ae478749a --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/plot.js @@ -0,0 +1,400 @@ +import * as theme from "./theme.js"; + +var infoIcon = { + 'width': 470, + 'height': 530, + 'path': 'M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z' +} + +const newConfigObject = (cfg) => { + return { + // showEditInChartStudio: true, + // plotlyServerURL: "https://chart-studio.plotly.com", + displaylogo: false, + modeBarButtonsToRemove: ['2D', 'zoom2d', 'pan2d', 'select2d', 'lasso2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d', 'toggleSpikelines'], + modeBarButtonsToAdd: [{ + name: 'info', + title: "Plot info", + icon: infoIcon, + val: false, + click: handleInfoButton, + },], + toImageButtonOptions: { + format: 'png', + filename: cfg.name, + } + } +} + +const copyArrayOrNull = (o) => { + return Array.isArray(o) && [...o] || null; +} + +const newLayoutObject = (cfg) => { + const layout = { + title: { + y: 0.88, + font: { + family: "Roboto", + size: 18, + }, + text: cfg.title, + }, + margin: { + t: 80, + }, + paper_bgcolor: cfg.layout.paper_bgcolor, + plot_bgcolor: cfg.layout.plot_bgcolor, + font: { + color: cfg.layout.font_color + }, + width: 630, + height: 450, + hovermode: 'x', + barmode: cfg.layout.barmode, + xaxis: { + tickformat: '%H:%M:%S', + type: "date", + fixedrange: true, + autorange: false, + }, + yaxis: { + exponentformat: 'SI', + tickmode: cfg.layout.yaxis.tickmode, + ticktext: copyArrayOrNull(cfg.layout.yaxis.ticktext), + tickvals: copyArrayOrNull(cfg.layout.yaxis.tickvals), + title: cfg.layout.yaxis.title, + ticksuffix: cfg.layout.yaxis.ticksuffix, + fixedrange: true, + }, + showlegend: true, + legend: { + "orientation": "h" + } + }; + + if (layout.yaxis.tickmode == "array") { + // Format yaxis ticks + const formatYUnit = formatFunction(cfg.hover.yunit); + for (let i = 0; i < layout.yaxis.ticktext.length; i++) { + layout.yaxis.ticktext[i] = formatYUnit(layout.yaxis.ticktext[i]); + } + } + + return layout; +} + +const handleInfoButton = (gd, ev) => { + let button = ev.currentTarget; + let val = (button.getAttribute('data-val') === 'true'); + + const options = { + allowHTML: true, + trigger: 'click', + }; + + const instance = tippy(ev.currentTarget, options); + instance.setContent("
" + gd.infoText + "
"); + if (val) { + instance.hide(); + } else { + instance.show(); + } + button.setAttribute('data-val', !val); +} + +const themeColors = { + light: { + paper_bgcolor: '#f8f8f8', + plot_bgcolor: '#ffffdd', + font_color: '#434343' + }, + dark: { + paper_bgcolor: '#181a1c', + plot_bgcolor: '#282a2c', + font_color: '#fff' + } +}; + +/* + Plot configuration object: + { + "name": string, // internal name + "title": string, // plot title + "type": 'scatter'|'bar'|'heatmap' + "updateFreq": int, // datapoints to receive before redrawing the plot. (default: 1) + "infoText": string, // text showed in the plot 'info' tooltip + "events": "lastgc", // source of vertical lines (example: 'lastgc') + "layout": object, // (depends on plot type) + "subplots": array, // describe 'traces', only for 'scatter' or 'bar' plots + "heatmap": object, // heatmap details + } + + Layout for 'scatter' and 'bar' plots: + { + "yaxis": { + "title": { + "text": "bytes" // yaxis title + }, + "ticksuffix": "B", // base unit for ticks + }, + "barmode": "stack", // 'stack' or 'group' (only for bar plots) + }, + + Layout" for heatmaps: + { + "yaxis": { + tickmode: string (supports 'array' only) + tickvals: []float64 + ticktext: []float64 + "title": { + "text": "size class" + } + } + + Subplots show the potentially multiple trace objects for 'scatter' and 'bar' + plots. Each trace is an object: + { + "name": string; // internal name + "unitfmt": string, // d3 format string for tooltip + "stackgroup": string, // stackgroup (if stacked line any) + "hoveron": string // useful for stacked only (TODO(arl): remove from go) + "color": colorstring // plot/trace color + } + + Heatmap details object + { + "colorscale": array // array of weighted colors, + "buckets": array + "hover": { + "yname": string, // y axis units + "yunit": "bytes", // y axis name + "zname": "objects" // z axis name + } + } +*/ + + +class Plot { + /** + * Construct a new Plot object, wrapping a Plotly chart. See above + * documentation for plot configuration. + */ + + constructor(cfg) { + cfg.layout.paper_bgcolor = themeColors[theme.getThemeMode()].paper_bgcolor; + cfg.layout.plot_bgcolor = themeColors[theme.getThemeMode()].plot_bgcolor; + cfg.layout.font_color = themeColors[theme.getThemeMode()].font_color; + + this._cfg = cfg; + this._updateCount = 0; + this._dataTemplate = []; + this._lastData = [{ x: new Date() }]; + + if (this._cfg.type == 'heatmap') { + this._dataTemplate.push({ + type: 'heatmap', + x: null, + y: this._cfg.buckets, + z: null, + showlegend: false, + colorscale: this._cfg.colorscale, + custom_data: this._cfg.custom_data, + }); + } else { + this._cfg.subplots.forEach(subplot => { + this._dataTemplate.push({ + type: this._cfg.type, + x: null, + y: null, + name: subplot.name, + hovertemplate: `${subplot.unitfmt}`, + }) + }); + } + + this._plotlyLayout = newLayoutObject(cfg); + this._plotlyConfig = newConfigObject(cfg); + } + + name() { + return this._cfg.name; + } + + createElement(div, idx) { + this._htmlElt = div; + this._plotIdx = idx; + // Pass a single data with no data to create an empty plot, this removes + // the 'bad time formatting' warning at startup. + Plotly.newPlot(this._htmlElt, this._lastData, this._plotlyLayout, this._plotlyConfig); + if (this._cfg.type == 'heatmap') { + this._installHeatmapTooltip(); + } + + this._htmlElt.infoText = this._cfg.infoText.split('\n').map(line => `

${line}

`).join(''); + } + + _installHeatmapTooltip() { + const options = { + followCursor: true, + trigger: "manual", + allowHTML: true + }; + const instance = tippy(document.body, options); + const hover = this._cfg.hover; + const formatYUnit = formatFunction(hover.yunit); + + const onHover = (data) => { + const pt2txt = (d) => { + let bucket; + if (d.y == 0) { + const yhigh = formatYUnit(d.data.custom_data[d.y]); + bucket = `(-Inf, ${yhigh})`; + } else if (d.y == d.data.custom_data.length - 1) { + const ylow = formatYUnit(d.data.custom_data[d.y]); + bucket = `[${ylow}, +Inf)`; + } else { + const ylow = formatYUnit(d.data.custom_data[d.y - 1]); + const yhigh = formatYUnit(d.data.custom_data[d.y]); + bucket = `[${ylow}, ${yhigh})`; + } + + return ` +
+
+
${hover.yname}
+
${bucket}
+
+
+
${hover.zname}
+
${d.z}
+
+
`; + } + instance.setContent(data.points.map(pt2txt)[0]); + instance.show(); + }; + const onUnhover = (data) => { + instance.hide(); + }; + + this._htmlElt.on('plotly_hover', onHover) + .on('plotly_unhover', onUnhover); + } + + _extractData(data) { + const serie = data.series.get(this._cfg.name); + if (this._cfg.type == 'heatmap') { + this._dataTemplate[0].x = data.times; + this._dataTemplate[0].z = serie; + this._dataTemplate[0].hoverinfo = 'none'; + } else { + for (let i = 0; i < this._dataTemplate.length; i++) { + this._dataTemplate[i].x = data.times; + this._dataTemplate[i].y = serie[i]; + this._dataTemplate[i].stackgroup = this._cfg.subplots[i].stackgroup; + this._dataTemplate[i].hoveron = this._cfg.subplots[i].hoveron; + this._dataTemplate[i].type = this._cfg.subplots[i].type || this._cfg.type; + this._dataTemplate[i].marker = { + color: this._cfg.subplots[i].color, + }; + } + } + return this._dataTemplate; + } + + update(xrange, data, shapes) { + this._lastData = this._extractData(data); + this._updateCount++; + if (this._cfg.updateFreq == 0 || (this._updateCount % this._cfg.updateFreq == 0)) { + // Update layout with vertical shapes if necessary. + if (this._cfg.events != '') { + this._plotlyLayout.shapes = shapes.get(this._cfg.events); + } + + // Move the xaxis time range. + this._plotlyLayout.xaxis.range = xrange; + + Plotly.react(this._htmlElt, this._lastData, this._plotlyLayout, this._plotlyConfig); + } + } + + /** + * update theme color and immediately force plot redraw to apply the new theme + */ + updateTheme() { + const themeMode = theme.getThemeMode(); + this._cfg.layout.paper_bgcolor = themeColors[themeMode].paper_bgcolor; + this._cfg.layout.plot_bgcolor = themeColors[themeMode].plot_bgcolor; + this._cfg.layout.font_color = themeColors[themeMode].font_color; + + this._plotlyLayout = newLayoutObject(this._cfg); + this._plotlyConfig = newConfigObject(this._cfg); + + Plotly.react(this._htmlElt, this._lastData, this._plotlyLayout); + } +}; + +// Create 'vertical lines' shapes for each of the given timestamps. +const createVerticalLines = (tss) => { + const shapes = []; + for (let i = 0, n = tss.length; i < n; i++) { + const d = tss[i]; + shapes.push({ + type: 'line', + x0: d, + x1: d, + yref: 'paper', + y0: 0, + y1: 1, + line: { + color: 'rgb(55, 128, 191)', + width: 1, + dash: 'longdashdot', + } + }) + } + return shapes; +} + +export { createVerticalLines, Plot }; + +const durUnits = ['w', 'd', 'h', 'm', 's', 'ms', 'µs', 'ns']; +const durVals = [6048e11, 864e11, 36e11, 6e10, 1e9, 1e6, 1e3, 1]; + +// Formats a time duration provided in second. +const formatDuration = sec => { + let ns = sec * 1e9; + for (let i = 0; i < durUnits.length; i++) { + let inc = ns / durVals[i]; + + if (inc < 1) continue; + return Math.round(inc) + durUnits[i]; + } + return res.trim(); +}; + +const bytesUnits = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB']; + +// Formats a size in bytes. +const formatBytes = bytes => { + let i = 0; + while (bytes > 1000) { + bytes /= 1000; + i++; + } + const res = Math.trunc(bytes); + return `${res}${bytesUnits[i]}`; +}; + +// Returns a format function based on the provided unit. +const formatFunction = unit => { + switch (unit) { + case 'duration': + return formatDuration; + case 'bytes': + return formatBytes; + } + // Default formatting + return (y) => { `${y} ${hover.yunit}` }; +}; diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/stats.js b/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/stats.js new file mode 100644 index 000000000..5e8c8809e --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/stats.js @@ -0,0 +1,95 @@ +// stats holds the data and function to modify it. +import Buffer from "./buffer.js"; + +var series = { + times: null, + eventsData: new Map(), + plotData: new Map(), +}; + +// initialize time series storage. +const init = (plotdefs, buflen) => { + const extraBufferCapacity = 20; // 20% of extra (preallocated) buffer datapoints + const bufcap = buflen + (buflen * extraBufferCapacity) / 100; // number of actual datapoints + + series.times = new Buffer(buflen, bufcap); + series.plotData.clear(); + plotdefs.series.forEach(plotdef => { + let ndim; + switch (plotdef.type) { + case 'bar': + case 'scatter': + ndim = plotdef.subplots.length; + break; + case 'heatmap': + ndim = plotdef.buckets.length; + break; + default: + console.error(`[statsviz]: unknown plot type "${plotdef.type}"`); + return; + }; + + let data = new Array(ndim); + for (let i = 0; i < ndim; i++) { + data[i] = new Buffer(buflen, bufcap); + } + series.plotData.set(plotdef.name, data); + }); + + plotdefs.events.forEach(event => { + series.eventsData.set(event, new Array()); + }); +} + +// push a new datapoint to all time series. +const pushData = (data) => { + series.times.push(data.timestamp); + + // Update time series. + for (const [name, plotData] of series.plotData) { + const curdata = data[name]; + for (let i = 0; i < curdata.length; i++) { + plotData[i].push(curdata[i]); + } + } + + // Update events series, deduplicating event timestamps and trimming the ones + // that are oldest with respect to the oldest timestamp we're keeping track of. + for (const [name, event] of series.eventsData) { + if (event.length == 0) { + if (data[name].length != 0) { + const eventTs = new Date(Math.floor(data[name][0])); + event.push(eventTs); + } + return; + } + const eventTs = new Date(Math.floor(data[name][0])); + if (eventTs.getTime() != event[event.length - 1].getTime()) { + event.push(eventTs); + let mints = series.times._buf[0]; + if (event[0] < mints) { + event.splice(0, 1); + } + } + } +} + +// slice returns the last n items from all time series. +const slice = (n) => { + let sliced = { + times: series.times.slice(n), + series: new Map(), + events: series.eventsData, + }; + + for (const [name, plotData] of series.plotData) { + const arr = new Array(plotData.length); + for (let i = 0; i < plotData.length; i++) { + arr[i] = plotData[i].slice(n); + } + sliced.series.set(name, arr); + } + return sliced; +} + +export { init, pushData, slice }; \ No newline at end of file diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/theme.js b/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/theme.js new file mode 100644 index 000000000..18498f3da --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/static/js/theme.js @@ -0,0 +1,36 @@ +/** + * Get color theme based on previous user choice or browser theme + */ +export const getThemeMode = () => { + let themeMode = localStorage.getItem("theme-mode"); + + if (themeMode === null) { + const isDark = + window.matchMedia && + window.matchMedia("(prefers-color-scheme: dark)").matches; + themeMode = (isDark && "dark") || "light"; + + localStorage.setItem("theme-mode", themeMode); + } + + return themeMode; +}; + +/** + * Set light or dark theme + */ +export const updateThemeMode = () => { + if (getThemeMode() === "dark") { + document.body.classList.add("dark-theme"); + document + .getElementById("navbar") + .classList.replace("navbar-light", "navbar-dark"); + document.getElementById("navbar").classList.replace("bg-light", "bg-dark"); + } else { + document.body.classList.remove("dark-theme"); + document + .getElementById("navbar") + .classList.replace("navbar-dark", "navbar-light"); + document.getElementById("navbar").classList.replace("bg-dark", "bg-light"); + } +}; diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/static/libs/css/bootstrap-toggle.min.css b/image-scanner/vendor/github.com/arl/statsviz/internal/static/libs/css/bootstrap-toggle.min.css new file mode 100644 index 000000000..0d42ed09c --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/static/libs/css/bootstrap-toggle.min.css @@ -0,0 +1,28 @@ +/*! ======================================================================== + * Bootstrap Toggle: bootstrap-toggle.css v2.2.0 + * http://www.bootstraptoggle.com + * ======================================================================== + * Copyright 2014 Min Hur, The New York Times Company + * Licensed under MIT + * ======================================================================== */ +.checkbox label .toggle,.checkbox-inline .toggle{margin-left:-20px;margin-right:5px} +.toggle{position:relative;overflow:hidden} +.toggle input[type=checkbox]{display:none} +.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none} +.toggle.off .toggle-group{left:-100%} +.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0} +.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0} +.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px} +.toggle.btn{min-width:59px;min-height:34px} +.toggle-on.btn{padding-right:24px} +.toggle-off.btn{padding-left:24px} +.toggle.btn-lg{min-width:79px;min-height:45px} +.toggle-on.btn-lg{padding-right:31px} +.toggle-off.btn-lg{padding-left:31px} +.toggle-handle.btn-lg{width:40px} +.toggle.btn-sm{min-width:50px;min-height:30px} +.toggle-on.btn-sm{padding-right:20px} +.toggle-off.btn-sm{padding-left:20px} +.toggle.btn-xs{min-width:35px;min-height:22px} +.toggle-on.btn-xs{padding-right:12px} +.toggle-off.btn-xs{padding-left:12px} \ No newline at end of file diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/static/libs/css/bootstrap.min.css b/image-scanner/vendor/github.com/arl/statsviz/internal/static/libs/css/bootstrap.min.css new file mode 100644 index 000000000..a18042d10 --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/static/libs/css/bootstrap.min.css @@ -0,0 +1,6 @@ +@charset "UTF-8";/*! + * Bootstrap v5.0.2 (https://getbootstrap.com/) + * Copyright 2011-2021 The Bootstrap Authors + * Copyright 2011-2021 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0))}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-font-sans-serif);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:#6c757d}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{width:100%;padding-right:var(--bs-gutter-x,.75rem);padding-left:var(--bs-gutter-x,.75rem);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(var(--bs-gutter-y) * -1);margin-right:calc(var(--bs-gutter-x) * -.5);margin-left:calc(var(--bs-gutter-x) * -.5)}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-bg:transparent;--bs-table-accent-bg:transparent;--bs-table-striped-color:#212529;--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:#212529;--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:#212529;--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:#212529;vertical-align:top;border-color:#dee2e6}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table>:not(:last-child)>:last-child>*{border-bottom-color:currentColor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-striped>tbody>tr:nth-of-type(odd){--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-bg:#cfe2ff;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:#000;border-color:#bacbe6}.table-secondary{--bs-table-bg:#e2e3e5;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:#000;border-color:#cbccce}.table-success{--bs-table-bg:#d1e7dd;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:#000;border-color:#bcd0c7}.table-info{--bs-table-bg:#cff4fc;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:#000;border-color:#badce3}.table-warning{--bs-table-bg:#fff3cd;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:#000;border-color:#e6dbb9}.table-danger{--bs-table-bg:#f8d7da;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:#000;border-color:#dfc2c4}.table-light{--bs-table-bg:#f8f9fa;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:#000;border-color:#dfe0e1}.table-dark{--bs-table-bg:#212529;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:#fff;border-color:#373b3e}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:#6c757d}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#212529;background-color:#fff;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + (.5rem + 2px));padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + (1rem + 2px));padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + (.75rem + 2px))}textarea.form-control-sm{min-height:calc(1.5em + (.5rem + 2px))}textarea.form-control-lg{min-height:calc(1.5em + (1rem + 2px))}.form-control-color{max-width:3rem;height:auto;padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{height:1.5em;border-radius:.25rem}.form-control-color::-webkit-color-swatch{height:1.5em;border-radius:.25rem}.form-select{display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;-moz-padding-start:calc(0.75rem - 3px);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #212529}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid rgba(0,0,0,.25);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{width:2em;margin-left:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px);line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;height:100%;padding:1rem .75rem;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control{padding:1rem .75rem}.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus{z-index:3}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:3}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#198754}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(25,135,84,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#198754;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:#198754}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:#198754}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:#198754}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#198754}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group .form-control.is-valid,.input-group .form-select.is-valid,.was-validated .input-group .form-control:valid,.was-validated .input-group .form-select:valid{z-index:1}.input-group .form-control.is-valid:focus,.input-group .form-select.is-valid:focus,.was-validated .input-group .form-control:valid:focus,.was-validated .input-group .form-select:valid:focus{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:#dc3545}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:#dc3545}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:#dc3545}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group .form-control.is-invalid,.input-group .form-select.is-invalid,.was-validated .input-group .form-control:invalid,.was-validated .input-group .form-select:invalid{z-index:2}.input-group .form-control.is-invalid:focus,.input-group .form-select.is-invalid:focus,.was-validated .input-group .form-control:invalid:focus,.was-validated .input-group .form-select:invalid:focus{z-index:3}.btn{display:inline-block;font-weight:400;line-height:1.5;color:#212529;text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529}.btn-check:focus+.btn,.btn:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{pointer-events:none;opacity:.65}.btn-primary{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-primary:hover{color:#fff;background-color:#0b5ed7;border-color:#0a58ca}.btn-check:focus+.btn-primary,.btn-primary:focus{color:#fff;background-color:#0b5ed7;border-color:#0a58ca;box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-check:active+.btn-primary,.btn-check:checked+.btn-primary,.btn-primary.active,.btn-primary:active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0a58ca;border-color:#0a53be}.btn-check:active+.btn-primary:focus,.btn-check:checked+.btn-primary:focus,.btn-primary.active:focus,.btn-primary:active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5c636a;border-color:#565e64}.btn-check:focus+.btn-secondary,.btn-secondary:focus{color:#fff;background-color:#5c636a;border-color:#565e64;box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-check:active+.btn-secondary,.btn-check:checked+.btn-secondary,.btn-secondary.active,.btn-secondary:active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#565e64;border-color:#51585e}.btn-check:active+.btn-secondary:focus,.btn-check:checked+.btn-secondary:focus,.btn-secondary.active:focus,.btn-secondary:active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-success{color:#fff;background-color:#198754;border-color:#198754}.btn-success:hover{color:#fff;background-color:#157347;border-color:#146c43}.btn-check:focus+.btn-success,.btn-success:focus{color:#fff;background-color:#157347;border-color:#146c43;box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-check:active+.btn-success,.btn-check:checked+.btn-success,.btn-success.active,.btn-success:active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#146c43;border-color:#13653f}.btn-check:active+.btn-success:focus,.btn-check:checked+.btn-success:focus,.btn-success.active:focus,.btn-success:active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#198754;border-color:#198754}.btn-info{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-info:hover{color:#000;background-color:#31d2f2;border-color:#25cff2}.btn-check:focus+.btn-info,.btn-info:focus{color:#000;background-color:#31d2f2;border-color:#25cff2;box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-check:active+.btn-info,.btn-check:checked+.btn-info,.btn-info.active,.btn-info:active,.show>.btn-info.dropdown-toggle{color:#000;background-color:#3dd5f3;border-color:#25cff2}.btn-check:active+.btn-info:focus,.btn-check:checked+.btn-info:focus,.btn-info.active:focus,.btn-info:active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-info.disabled,.btn-info:disabled{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-warning{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#000;background-color:#ffca2c;border-color:#ffc720}.btn-check:focus+.btn-warning,.btn-warning:focus{color:#000;background-color:#ffca2c;border-color:#ffc720;box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-check:active+.btn-warning,.btn-check:checked+.btn-warning,.btn-warning.active,.btn-warning:active,.show>.btn-warning.dropdown-toggle{color:#000;background-color:#ffcd39;border-color:#ffc720}.btn-check:active+.btn-warning:focus,.btn-check:checked+.btn-warning:focus,.btn-warning.active:focus,.btn-warning:active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#bb2d3b;border-color:#b02a37}.btn-check:focus+.btn-danger,.btn-danger:focus{color:#fff;background-color:#bb2d3b;border-color:#b02a37;box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-check:active+.btn-danger,.btn-check:checked+.btn-danger,.btn-danger.active,.btn-danger:active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#b02a37;border-color:#a52834}.btn-check:active+.btn-danger:focus,.btn-check:checked+.btn-danger:focus,.btn-danger.active:focus,.btn-danger:active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-light{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:focus+.btn-light,.btn-light:focus{color:#000;background-color:#f9fafb;border-color:#f9fafb;box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-check:active+.btn-light,.btn-check:checked+.btn-light,.btn-light.active,.btn-light:active,.show>.btn-light.dropdown-toggle{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:active+.btn-light:focus,.btn-check:checked+.btn-light:focus,.btn-light.active:focus,.btn-light:active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-light.disabled,.btn-light:disabled{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-dark{color:#fff;background-color:#212529;border-color:#212529}.btn-dark:hover{color:#fff;background-color:#1c1f23;border-color:#1a1e21}.btn-check:focus+.btn-dark,.btn-dark:focus{color:#fff;background-color:#1c1f23;border-color:#1a1e21;box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-check:active+.btn-dark,.btn-check:checked+.btn-dark,.btn-dark.active,.btn-dark:active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1a1e21;border-color:#191c1f}.btn-check:active+.btn-dark:focus,.btn-check:checked+.btn-dark:focus,.btn-dark.active:focus,.btn-dark:active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#212529;border-color:#212529}.btn-outline-primary{color:#0d6efd;border-color:#0d6efd}.btn-outline-primary:hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-check:active+.btn-outline-primary,.btn-check:checked+.btn-outline-primary,.btn-outline-primary.active,.btn-outline-primary.dropdown-toggle.show,.btn-outline-primary:active{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:active+.btn-outline-primary:focus,.btn-check:checked+.btn-outline-primary:focus,.btn-outline-primary.active:focus,.btn-outline-primary.dropdown-toggle.show:focus,.btn-outline-primary:active:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#0d6efd;background-color:transparent}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-check:active+.btn-outline-secondary,.btn-check:checked+.btn-outline-secondary,.btn-outline-secondary.active,.btn-outline-secondary.dropdown-toggle.show,.btn-outline-secondary:active{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:active+.btn-outline-secondary:focus,.btn-check:checked+.btn-outline-secondary:focus,.btn-outline-secondary.active:focus,.btn-outline-secondary.dropdown-toggle.show:focus,.btn-outline-secondary:active:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-success{color:#198754;border-color:#198754}.btn-outline-success:hover{color:#fff;background-color:#198754;border-color:#198754}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-check:active+.btn-outline-success,.btn-check:checked+.btn-outline-success,.btn-outline-success.active,.btn-outline-success.dropdown-toggle.show,.btn-outline-success:active{color:#fff;background-color:#198754;border-color:#198754}.btn-check:active+.btn-outline-success:focus,.btn-check:checked+.btn-outline-success:focus,.btn-outline-success.active:focus,.btn-outline-success.dropdown-toggle.show:focus,.btn-outline-success:active:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#198754;background-color:transparent}.btn-outline-info{color:#0dcaf0;border-color:#0dcaf0}.btn-outline-info:hover{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-check:active+.btn-outline-info,.btn-check:checked+.btn-outline-info,.btn-outline-info.active,.btn-outline-info.dropdown-toggle.show,.btn-outline-info:active{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:active+.btn-outline-info:focus,.btn-check:checked+.btn-outline-info:focus,.btn-outline-info.active:focus,.btn-outline-info.dropdown-toggle.show:focus,.btn-outline-info:active:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#0dcaf0;background-color:transparent}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-check:active+.btn-outline-warning,.btn-check:checked+.btn-outline-warning,.btn-outline-warning.active,.btn-outline-warning.dropdown-toggle.show,.btn-outline-warning:active{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:active+.btn-outline-warning:focus,.btn-check:checked+.btn-outline-warning:focus,.btn-outline-warning.active:focus,.btn-outline-warning.dropdown-toggle.show:focus,.btn-outline-warning:active:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-check:active+.btn-outline-danger,.btn-check:checked+.btn-outline-danger,.btn-outline-danger.active,.btn-outline-danger.dropdown-toggle.show,.btn-outline-danger:active{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:active+.btn-outline-danger:focus,.btn-check:checked+.btn-outline-danger:focus,.btn-outline-danger.active:focus,.btn-outline-danger.dropdown-toggle.show:focus,.btn-outline-danger:active:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-check:active+.btn-outline-light,.btn-check:checked+.btn-outline-light,.btn-outline-light.active,.btn-outline-light.dropdown-toggle.show,.btn-outline-light:active{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:active+.btn-outline-light:focus,.btn-check:checked+.btn-outline-light:focus,.btn-outline-light.active:focus,.btn-outline-light.dropdown-toggle.show:focus,.btn-outline-light:active:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-dark{color:#212529;border-color:#212529}.btn-outline-dark:hover{color:#fff;background-color:#212529;border-color:#212529}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-check:active+.btn-outline-dark,.btn-check:checked+.btn-outline-dark,.btn-outline-dark.active,.btn-outline-dark.dropdown-toggle.show,.btn-outline-dark:active{color:#fff;background-color:#212529;border-color:#212529}.btn-check:active+.btn-outline-dark:focus,.btn-check:checked+.btn-outline-dark:focus,.btn-outline-dark.active:focus,.btn-outline-dark.dropdown-toggle.show:focus,.btn-outline-dark:active:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#212529;background-color:transparent}.btn-link{font-weight:400;color:#0d6efd;text-decoration:underline}.btn-link:hover{color:#0a58ca}.btn-link.disabled,.btn-link:disabled{color:#6c757d}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropend,.dropstart,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;z-index:1000;display:none;min-width:10rem;padding:.5rem 0;margin:0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:.125rem}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15)}.dropdown-item{display:block;width:100%;padding:.25rem 1rem;clear:both;font-weight:400;color:#212529;text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#1e2125;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#0d6efd}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1rem;color:#212529}.dropdown-menu-dark{color:#dee2e6;background-color:#343a40;border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item{color:#dee2e6}.dropdown-menu-dark .dropdown-item:focus,.dropdown-menu-dark .dropdown-item:hover{color:#fff;background-color:rgba(255,255,255,.15)}.dropdown-menu-dark .dropdown-item.active,.dropdown-menu-dark .dropdown-item:active{color:#fff;background-color:#0d6efd}.dropdown-menu-dark .dropdown-item.disabled,.dropdown-menu-dark .dropdown-item:disabled{color:#adb5bd}.dropdown-menu-dark .dropdown-divider{border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item-text{color:#dee2e6}.dropdown-menu-dark .dropdown-header{color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem;color:#0d6efd;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:#0a58ca}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background:0 0;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6;isolation:isolate}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#0d6efd}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;text-decoration:none;white-space:nowrap}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 .25rem}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.55)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.55);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.55)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.55)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.55);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.55)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:1rem 1rem}.card-title{margin-bottom:.5rem}.card-subtitle{margin-top:-.25rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1rem}.card-header{padding:.5rem 1rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.5rem 1rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.5rem;margin-bottom:-.5rem;margin-left:-.5rem;border-bottom:0}.card-header-pills{margin-right:-.5rem;margin-left:-.5rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-group>.card{margin-bottom:.75rem}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:1rem 1.25rem;font-size:1rem;color:#212529;text-align:left;background-color:#fff;border:0;border-radius:0;overflow-anchor:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:#0c63e4;background-color:#e7f1ff;box-shadow:inset 0 -1px 0 rgba(0,0,0,.125)}.accordion-button:not(.collapsed)::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");transform:rotate(-180deg)}.accordion-button::after{flex-shrink:0;width:1.25rem;height:1.25rem;margin-left:auto;content:"";background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-size:1.25rem;transition:transform .2s ease-in-out}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.accordion-header{margin-bottom:0}.accordion-item{background-color:#fff;border:1px solid rgba(0,0,0,.125)}.accordion-item:first-of-type{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.accordion-item:first-of-type .accordion-button{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-body{padding:1rem 1.25rem}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button{border-radius:0}.breadcrumb{display:flex;flex-wrap:wrap;padding:0 0;margin-bottom:1rem;list-style:none}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;color:#0d6efd;text-decoration:none;background-color:#fff;border:1px solid #dee2e6;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:#0a58ca;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;color:#0a58ca;background-color:#e9ecef;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item.active .page-link{z-index:3;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;background-color:#fff;border-color:#dee2e6}.page-link{padding:.375rem .75rem}.page-item:first-child .page-link{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{position:relative;padding:1rem 1rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{color:#084298;background-color:#cfe2ff;border-color:#b6d4fe}.alert-primary .alert-link{color:#06357a}.alert-secondary{color:#41464b;background-color:#e2e3e5;border-color:#d3d6d8}.alert-secondary .alert-link{color:#34383c}.alert-success{color:#0f5132;background-color:#d1e7dd;border-color:#badbcc}.alert-success .alert-link{color:#0c4128}.alert-info{color:#055160;background-color:#cff4fc;border-color:#b6effb}.alert-info .alert-link{color:#04414d}.alert-warning{color:#664d03;background-color:#fff3cd;border-color:#ffecb5}.alert-warning .alert-link{color:#523e02}.alert-danger{color:#842029;background-color:#f8d7da;border-color:#f5c2c7}.alert-danger .alert-link{color:#6a1a21}.alert-light{color:#636464;background-color:#fefefe;border-color:#fdfdfe}.alert-light .alert-link{color:#4f5050}.alert-dark{color:#141619;background-color:#d3d3d4;border-color:#bcbebf}.alert-dark .alert-link{color:#101214}@-webkit-keyframes progress-bar-stripes{0%{background-position-x:1rem}}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#0d6efd;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>li::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.5rem 1rem;color:#212529;text-decoration:none;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#084298;background-color:#cfe2ff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#084298;background-color:#bacbe6}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#084298;border-color:#084298}.list-group-item-secondary{color:#41464b;background-color:#e2e3e5}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#41464b;background-color:#cbccce}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#41464b;border-color:#41464b}.list-group-item-success{color:#0f5132;background-color:#d1e7dd}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#0f5132;background-color:#bcd0c7}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#0f5132;border-color:#0f5132}.list-group-item-info{color:#055160;background-color:#cff4fc}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#055160;background-color:#badce3}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#055160;border-color:#055160}.list-group-item-warning{color:#664d03;background-color:#fff3cd}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#664d03;background-color:#e6dbb9}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#664d03;border-color:#664d03}.list-group-item-danger{color:#842029;background-color:#f8d7da}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#842029;background-color:#dfc2c4}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#842029;border-color:#842029}.list-group-item-light{color:#636464;background-color:#fefefe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#636464;background-color:#e5e5e5}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#636464;border-color:#636464}.list-group-item-dark{color:#141619;background-color:#d3d3d4}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#141619;background-color:#bebebf}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:#000;background:transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;border:0;border-radius:.25rem;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);opacity:1}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:.25}.btn-close-white{filter:invert(1) grayscale(100%) brightness(200%)}.toast{width:350px;max-width:100%;font-size:.875rem;pointer-events:auto;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .5rem 1rem rgba(0,0,0,.15);border-radius:.25rem}.toast:not(.showing):not(.show){opacity:0}.toast.hide{display:none}.toast-container{width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:.75rem}.toast-header{display:flex;align-items:center;padding:.5rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-header .btn-close{margin-right:-.375rem;margin-left:.75rem}.toast-body{padding:.75rem;word-wrap:break-word}.modal{position:fixed;top:0;left:0;z-index:1060;display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .btn-close{padding:.5rem .5rem;margin:-.5rem -.5rem -.5rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;flex-shrink:0;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{height:calc(100% - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}.modal-fullscreen .modal-footer{border-radius:0}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}.modal-fullscreen-sm-down .modal-footer{border-radius:0}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}.modal-fullscreen-md-down .modal-footer{border-radius:0}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}.modal-fullscreen-lg-down .modal-footer{border-radius:0}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}.modal-fullscreen-xl-down .modal-footer{border-radius:0}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}.modal-fullscreen-xxl-down .modal-footer{border-radius:0}}.tooltip{position:absolute;z-index:1080;display:block;margin:0;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .tooltip-arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[data-popper-placement^=right],.bs-tooltip-end{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[data-popper-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[data-popper-placement^=left],.bs-tooltip-start{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1070;display:block;max-width:276px;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .popover-arrow{position:absolute;display:block;width:1rem;height:.5rem}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f0f0f0}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem 1rem;margin-bottom:0;font-size:1rem;background-color:#f0f0f0;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:1rem 1rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;list-style:none}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}@-webkit-keyframes spinner-border{to{transform:rotate(360deg)}}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.offcanvas{position:fixed;bottom:0;z-index:1050;display:flex;flex-direction:column;max-width:100%;visibility:hidden;background-color:#fff;background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1rem}.offcanvas-header .btn-close{padding:.5rem .5rem;margin-top:-.5rem;margin-right:-.5rem;margin-bottom:-.5rem}.offcanvas-title{margin-bottom:0;line-height:1.5}.offcanvas-body{flex-grow:1;padding:1rem 1rem;overflow-y:auto}.offcanvas-start{top:0;left:0;width:400px;border-right:1px solid rgba(0,0,0,.2);transform:translateX(-100%)}.offcanvas-end{top:0;right:0;width:400px;border-left:1px solid rgba(0,0,0,.2);transform:translateX(100%)}.offcanvas-top{top:0;right:0;left:0;height:30vh;max-height:100%;border-bottom:1px solid rgba(0,0,0,.2);transform:translateY(-100%)}.offcanvas-bottom{right:0;left:0;height:30vh;max-height:100%;border-top:1px solid rgba(0,0,0,.2);transform:translateY(100%)}.offcanvas.show{transform:none}.clearfix::after{display:block;clear:both;content:""}.link-primary{color:#0d6efd}.link-primary:focus,.link-primary:hover{color:#0a58ca}.link-secondary{color:#6c757d}.link-secondary:focus,.link-secondary:hover{color:#565e64}.link-success{color:#198754}.link-success:focus,.link-success:hover{color:#146c43}.link-info{color:#0dcaf0}.link-info:focus,.link-info:hover{color:#3dd5f3}.link-warning{color:#ffc107}.link-warning:focus,.link-warning:hover{color:#ffcd39}.link-danger{color:#dc3545}.link-danger:focus,.link-danger:hover{color:#b02a37}.link-light{color:#f8f9fa}.link-light:focus,.link-light:hover{color:#f9fafb}.link-dark{color:#212529}.link-dark:focus,.link-dark:hover{color:#1a1e21}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:calc(3 / 4 * 100%)}.ratio-16x9{--bs-aspect-ratio:calc(9 / 16 * 100%)}.ratio-21x9{--bs-aspect-ratio:calc(9 / 21 * 100%)}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:1px solid #dee2e6!important}.border-0{border:0!important}.border-top{border-top:1px solid #dee2e6!important}.border-top-0{border-top:0!important}.border-end{border-right:1px solid #dee2e6!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:1px solid #dee2e6!important}.border-start-0{border-left:0!important}.border-primary{border-color:#0d6efd!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#198754!important}.border-info{border-color:#0dcaf0!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#212529!important}.border-white{border-color:#fff!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-light{font-weight:300!important}.fw-lighter{font-weight:lighter!important}.fw-normal{font-weight:400!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{color:#0d6efd!important}.text-secondary{color:#6c757d!important}.text-success{color:#198754!important}.text-info{color:#0dcaf0!important}.text-warning{color:#ffc107!important}.text-danger{color:#dc3545!important}.text-light{color:#f8f9fa!important}.text-dark{color:#212529!important}.text-white{color:#fff!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-reset{color:inherit!important}.bg-primary{background-color:#0d6efd!important}.bg-secondary{background-color:#6c757d!important}.bg-success{background-color:#198754!important}.bg-info{background-color:#0dcaf0!important}.bg-warning{background-color:#ffc107!important}.bg-danger{background-color:#dc3545!important}.bg-light{background-color:#f8f9fa!important}.bg-dark{background-color:#212529!important}.bg-body{background-color:#fff!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:.25rem!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:.2rem!important}.rounded-2{border-radius:.25rem!important}.rounded-3{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-end{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-start{border-bottom-left-radius:.25rem!important;border-top-left-radius:.25rem!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/static/libs/css/font.awesome-6.1.2-all.min.css b/image-scanner/vendor/github.com/arl/statsviz/internal/static/libs/css/font.awesome-6.1.2-all.min.css new file mode 100644 index 000000000..b9a01fbb7 --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/static/libs/css/font.awesome-6.1.2-all.min.css @@ -0,0 +1,6 @@ +/*! + * Font Awesome Free 6.1.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2022 Fonticons, Inc. + */ +.fa{font-family:var(--fa-style-family,"Font Awesome 6 Free");font-weight:var(--fa-style,900)}.fa,.fa-brands,.fa-duotone,.fa-light,.fa-regular,.fa-solid,.fa-thin,.fab,.fad,.fal,.far,.fas,.fat{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display,inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.08333em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.07143em;vertical-align:.05357em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.04167em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin,2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em)*-1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-radius:var(--fa-border-radius,.1em);border:var(--fa-border-width,.08em) var(--fa-border-style,solid) var(--fa-border-color,#eee);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{-webkit-animation-name:fa-beat;animation-name:fa-beat;-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{-webkit-animation-name:fa-bounce;animation-name:fa-bounce;-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{-webkit-animation-name:fa-fade;animation-name:fa-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade,.fa-fade{-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s)}.fa-beat-fade{-webkit-animation-name:fa-beat-fade;animation-name:fa-beat-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{-webkit-animation-name:fa-flip;animation-name:fa-flip;-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{-webkit-animation-name:fa-shake;animation-name:fa-shake;-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-shake,.fa-spin{-webkit-animation-delay:var(--fa-animation-delay,0);animation-delay:var(--fa-animation-delay,0);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal)}.fa-spin{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-duration:var(--fa-animation-duration,2s);animation-duration:var(--fa-animation-duration,2s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,steps(8));animation-timing-function:var(--fa-animation-timing,steps(8))}@media (prefers-reduced-motion:reduce){.fa-beat,.fa-beat-fade,.fa-bounce,.fa-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{-webkit-animation-delay:-1ms;animation-delay:-1ms;-webkit-animation-duration:1ms;animation-duration:1ms;-webkit-animation-iteration-count:1;animation-iteration-count:1;transition-delay:0s;transition-duration:0s}}@-webkit-keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@-webkit-keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@-webkit-keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@-webkit-keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@-webkit-keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@-webkit-keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}.fa-rotate-by{-webkit-transform:rotate(var(--fa-rotate-angle,none));transform:rotate(var(--fa-rotate-angle,none))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index,auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse,#fff)}.fa-0:before{content:"\30"}.fa-1:before{content:"\31"}.fa-2:before{content:"\32"}.fa-3:before{content:"\33"}.fa-4:before{content:"\34"}.fa-5:before{content:"\35"}.fa-6:before{content:"\36"}.fa-7:before{content:"\37"}.fa-8:before{content:"\38"}.fa-9:before{content:"\39"}.fa-a:before{content:"\41"}.fa-address-book:before,.fa-contact-book:before{content:"\f2b9"}.fa-address-card:before,.fa-contact-card:before,.fa-vcard:before{content:"\f2bb"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-anchor:before{content:"\f13d"}.fa-anchor-circle-check:before{content:"\e4aa"}.fa-anchor-circle-exclamation:before{content:"\e4ab"}.fa-anchor-circle-xmark:before{content:"\e4ac"}.fa-anchor-lock:before{content:"\e4ad"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-double-down:before,.fa-angles-down:before{content:"\f103"}.fa-angle-double-left:before,.fa-angles-left:before{content:"\f100"}.fa-angle-double-right:before,.fa-angles-right:before{content:"\f101"}.fa-angle-double-up:before,.fa-angles-up:before{content:"\f102"}.fa-ankh:before{content:"\f644"}.fa-apple-alt:before,.fa-apple-whole:before{content:"\f5d1"}.fa-archway:before{content:"\f557"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-down-1-9:before,.fa-sort-numeric-asc:before,.fa-sort-numeric-down:before{content:"\f162"}.fa-arrow-down-9-1:before,.fa-sort-numeric-desc:before,.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-arrow-down-a-z:before,.fa-sort-alpha-asc:before,.fa-sort-alpha-down:before{content:"\f15d"}.fa-arrow-down-long:before,.fa-long-arrow-down:before{content:"\f175"}.fa-arrow-down-short-wide:before,.fa-sort-amount-desc:before,.fa-sort-amount-down-alt:before{content:"\f884"}.fa-arrow-down-up-across-line:before{content:"\e4af"}.fa-arrow-down-up-lock:before{content:"\e4b0"}.fa-arrow-down-wide-short:before,.fa-sort-amount-asc:before,.fa-sort-amount-down:before{content:"\f160"}.fa-arrow-down-z-a:before,.fa-sort-alpha-desc:before,.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-left-long:before,.fa-long-arrow-left:before{content:"\f177"}.fa-arrow-pointer:before,.fa-mouse-pointer:before{content:"\f245"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-right-arrow-left:before,.fa-exchange:before{content:"\f0ec"}.fa-arrow-right-from-bracket:before,.fa-sign-out:before{content:"\f08b"}.fa-arrow-right-long:before,.fa-long-arrow-right:before{content:"\f178"}.fa-arrow-right-to-bracket:before,.fa-sign-in:before{content:"\f090"}.fa-arrow-right-to-city:before{content:"\e4b3"}.fa-arrow-left-rotate:before,.fa-arrow-rotate-back:before,.fa-arrow-rotate-backward:before,.fa-arrow-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-arrow-right-rotate:before,.fa-arrow-rotate-forward:before,.fa-arrow-rotate-right:before,.fa-redo:before{content:"\f01e"}.fa-arrow-trend-down:before{content:"\e097"}.fa-arrow-trend-up:before{content:"\e098"}.fa-arrow-turn-down:before,.fa-level-down:before{content:"\f149"}.fa-arrow-turn-up:before,.fa-level-up:before{content:"\f148"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-up-1-9:before,.fa-sort-numeric-up:before{content:"\f163"}.fa-arrow-up-9-1:before,.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-arrow-up-a-z:before,.fa-sort-alpha-up:before{content:"\f15e"}.fa-arrow-up-from-bracket:before{content:"\e09a"}.fa-arrow-up-from-ground-water:before{content:"\e4b5"}.fa-arrow-up-from-water-pump:before{content:"\e4b6"}.fa-arrow-up-long:before,.fa-long-arrow-up:before{content:"\f176"}.fa-arrow-up-right-dots:before{content:"\e4b7"}.fa-arrow-up-right-from-square:before,.fa-external-link:before{content:"\f08e"}.fa-arrow-up-short-wide:before,.fa-sort-amount-up-alt:before{content:"\f885"}.fa-arrow-up-wide-short:before,.fa-sort-amount-up:before{content:"\f161"}.fa-arrow-up-z-a:before,.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-arrows-down-to-line:before{content:"\e4b8"}.fa-arrows-down-to-people:before{content:"\e4b9"}.fa-arrows-h:before,.fa-arrows-left-right:before{content:"\f07e"}.fa-arrows-left-right-to-line:before{content:"\e4ba"}.fa-arrows-rotate:before,.fa-refresh:before,.fa-sync:before{content:"\f021"}.fa-arrows-spin:before{content:"\e4bb"}.fa-arrows-split-up-and-left:before{content:"\e4bc"}.fa-arrows-to-circle:before{content:"\e4bd"}.fa-arrows-to-dot:before{content:"\e4be"}.fa-arrows-to-eye:before{content:"\e4bf"}.fa-arrows-turn-right:before{content:"\e4c0"}.fa-arrows-turn-to-dots:before{content:"\e4c1"}.fa-arrows-up-down:before,.fa-arrows-v:before{content:"\f07d"}.fa-arrows-up-down-left-right:before,.fa-arrows:before{content:"\f047"}.fa-arrows-up-to-line:before{content:"\e4c2"}.fa-asterisk:before{content:"\2a"}.fa-at:before{content:"\40"}.fa-atom:before{content:"\f5d2"}.fa-audio-description:before{content:"\f29e"}.fa-austral-sign:before{content:"\e0a9"}.fa-award:before{content:"\f559"}.fa-b:before{content:"\42"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before,.fa-carriage-baby:before{content:"\f77d"}.fa-backward:before{content:"\f04a"}.fa-backward-fast:before,.fa-fast-backward:before{content:"\f049"}.fa-backward-step:before,.fa-step-backward:before{content:"\f048"}.fa-bacon:before{content:"\f7e5"}.fa-bacteria:before{content:"\e059"}.fa-bacterium:before{content:"\e05a"}.fa-bag-shopping:before,.fa-shopping-bag:before{content:"\f290"}.fa-bahai:before,.fa-haykal:before{content:"\f666"}.fa-baht-sign:before{content:"\e0ac"}.fa-ban:before,.fa-cancel:before{content:"\f05e"}.fa-ban-smoking:before,.fa-smoking-ban:before{content:"\f54d"}.fa-band-aid:before,.fa-bandage:before{content:"\f462"}.fa-barcode:before{content:"\f02a"}.fa-bars:before,.fa-navicon:before{content:"\f0c9"}.fa-bars-progress:before,.fa-tasks-alt:before{content:"\f828"}.fa-bars-staggered:before,.fa-reorder:before,.fa-stream:before{content:"\f550"}.fa-baseball-ball:before,.fa-baseball:before{content:"\f433"}.fa-baseball-bat-ball:before{content:"\f432"}.fa-basket-shopping:before,.fa-shopping-basket:before{content:"\f291"}.fa-basketball-ball:before,.fa-basketball:before{content:"\f434"}.fa-bath:before,.fa-bathtub:before{content:"\f2cd"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-battery-5:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-battery-3:before,.fa-battery-half:before{content:"\f242"}.fa-battery-2:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-4:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-bed:before{content:"\f236"}.fa-bed-pulse:before,.fa-procedures:before{content:"\f487"}.fa-beer-mug-empty:before,.fa-beer:before{content:"\f0fc"}.fa-bell:before{content:"\f0f3"}.fa-bell-concierge:before,.fa-concierge-bell:before{content:"\f562"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bicycle:before{content:"\f206"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-bitcoin-sign:before{content:"\e0b4"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blog:before{content:"\f781"}.fa-bold:before{content:"\f032"}.fa-bolt:before,.fa-zap:before{content:"\f0e7"}.fa-bolt-lightning:before{content:"\e0b7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-atlas:before,.fa-book-atlas:before{content:"\f558"}.fa-bible:before,.fa-book-bible:before{content:"\f647"}.fa-book-bookmark:before{content:"\e0bb"}.fa-book-journal-whills:before,.fa-journal-whills:before{content:"\f66a"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-open-reader:before,.fa-book-reader:before{content:"\f5da"}.fa-book-quran:before,.fa-quran:before{content:"\f687"}.fa-book-dead:before,.fa-book-skull:before{content:"\f6b7"}.fa-book-tanakh:before,.fa-tanakh:before{content:"\f827"}.fa-bookmark:before{content:"\f02e"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before,.fa-border-top-left:before{content:"\f853"}.fa-bore-hole:before{content:"\e4c3"}.fa-bottle-droplet:before{content:"\e4c4"}.fa-bottle-water:before{content:"\e4c5"}.fa-bowl-food:before{content:"\e4c6"}.fa-bowl-rice:before{content:"\e2eb"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-archive:before,.fa-box-archive:before{content:"\f187"}.fa-box-open:before{content:"\f49e"}.fa-box-tissue:before{content:"\e05b"}.fa-boxes-packing:before{content:"\e4c7"}.fa-boxes-alt:before,.fa-boxes-stacked:before,.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-brazilian-real-sign:before{content:"\e46c"}.fa-bread-slice:before{content:"\f7ec"}.fa-bridge:before{content:"\e4c8"}.fa-bridge-circle-check:before{content:"\e4c9"}.fa-bridge-circle-exclamation:before{content:"\e4ca"}.fa-bridge-circle-xmark:before{content:"\e4cb"}.fa-bridge-lock:before{content:"\e4cc"}.fa-bridge-water:before{content:"\e4ce"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broom:before{content:"\f51a"}.fa-broom-ball:before,.fa-quidditch-broom-ball:before,.fa-quidditch:before{content:"\f458"}.fa-brush:before{content:"\f55d"}.fa-bucket:before{content:"\e4cf"}.fa-bug:before{content:"\f188"}.fa-bug-slash:before{content:"\e490"}.fa-bugs:before{content:"\e4d0"}.fa-building:before{content:"\f1ad"}.fa-building-circle-arrow-right:before{content:"\e4d1"}.fa-building-circle-check:before{content:"\e4d2"}.fa-building-circle-exclamation:before{content:"\e4d3"}.fa-building-circle-xmark:before{content:"\e4d4"}.fa-bank:before,.fa-building-columns:before,.fa-institution:before,.fa-museum:before,.fa-university:before{content:"\f19c"}.fa-building-flag:before{content:"\e4d5"}.fa-building-lock:before{content:"\e4d6"}.fa-building-ngo:before{content:"\e4d7"}.fa-building-shield:before{content:"\e4d8"}.fa-building-un:before{content:"\e4d9"}.fa-building-user:before{content:"\e4da"}.fa-building-wheat:before{content:"\e4db"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burger:before,.fa-hamburger:before{content:"\f805"}.fa-burst:before{content:"\e4dc"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before,.fa-bus-simple:before{content:"\f55e"}.fa-briefcase-clock:before,.fa-business-time:before{content:"\f64a"}.fa-c:before{content:"\43"}.fa-cable-car:before,.fa-tram:before{content:"\f7da"}.fa-birthday-cake:before,.fa-cake-candles:before,.fa-cake:before{content:"\f1fd"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-alt:before,.fa-calendar-days:before{content:"\f073"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-week:before{content:"\f784"}.fa-calendar-times:before,.fa-calendar-xmark:before{content:"\f273"}.fa-camera-alt:before,.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-camera-rotate:before{content:"\e0d8"}.fa-campground:before{content:"\f6bb"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-battery-car:before,.fa-car-battery:before{content:"\f5df"}.fa-car-burst:before,.fa-car-crash:before{content:"\f5e1"}.fa-car-on:before{content:"\e4dd"}.fa-car-alt:before,.fa-car-rear:before{content:"\f5de"}.fa-car-side:before{content:"\f5e4"}.fa-car-tunnel:before{content:"\e4de"}.fa-caravan:before{content:"\f8ff"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-flatbed:before,.fa-dolly-flatbed:before{content:"\f474"}.fa-cart-flatbed-suitcase:before,.fa-luggage-cart:before{content:"\f59d"}.fa-cart-plus:before{content:"\f217"}.fa-cart-shopping:before,.fa-shopping-cart:before{content:"\f07a"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cedi-sign:before{content:"\e0df"}.fa-cent-sign:before{content:"\e3f5"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-blackboard:before,.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before,.fa-chalkboard-user:before{content:"\f51c"}.fa-champagne-glasses:before,.fa-glass-cheers:before{content:"\f79f"}.fa-charging-station:before{content:"\f5e7"}.fa-area-chart:before,.fa-chart-area:before{content:"\f1fe"}.fa-bar-chart:before,.fa-chart-bar:before{content:"\f080"}.fa-chart-column:before{content:"\e0e3"}.fa-chart-gantt:before{content:"\e0e4"}.fa-chart-line:before,.fa-line-chart:before{content:"\f201"}.fa-chart-pie:before,.fa-pie-chart:before{content:"\f200"}.fa-chart-simple:before{content:"\e473"}.fa-check:before{content:"\f00c"}.fa-check-double:before{content:"\f560"}.fa-check-to-slot:before,.fa-vote-yea:before{content:"\f772"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-child-dress:before{content:"\e59c"}.fa-child-reaching:before{content:"\e59d"}.fa-child-rifle:before{content:"\e4e0"}.fa-children:before{content:"\e4e1"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-arrow-circle-down:before,.fa-circle-arrow-down:before{content:"\f0ab"}.fa-arrow-circle-left:before,.fa-circle-arrow-left:before{content:"\f0a8"}.fa-arrow-circle-right:before,.fa-circle-arrow-right:before{content:"\f0a9"}.fa-arrow-circle-up:before,.fa-circle-arrow-up:before{content:"\f0aa"}.fa-check-circle:before,.fa-circle-check:before{content:"\f058"}.fa-chevron-circle-down:before,.fa-circle-chevron-down:before{content:"\f13a"}.fa-chevron-circle-left:before,.fa-circle-chevron-left:before{content:"\f137"}.fa-chevron-circle-right:before,.fa-circle-chevron-right:before{content:"\f138"}.fa-chevron-circle-up:before,.fa-circle-chevron-up:before{content:"\f139"}.fa-circle-dollar-to-slot:before,.fa-donate:before{content:"\f4b9"}.fa-circle-dot:before,.fa-dot-circle:before{content:"\f192"}.fa-arrow-alt-circle-down:before,.fa-circle-down:before{content:"\f358"}.fa-circle-exclamation:before,.fa-exclamation-circle:before{content:"\f06a"}.fa-circle-h:before,.fa-hospital-symbol:before{content:"\f47e"}.fa-adjust:before,.fa-circle-half-stroke:before{content:"\f042"}.fa-circle-info:before,.fa-info-circle:before{content:"\f05a"}.fa-arrow-alt-circle-left:before,.fa-circle-left:before{content:"\f359"}.fa-circle-minus:before,.fa-minus-circle:before{content:"\f056"}.fa-circle-nodes:before{content:"\e4e2"}.fa-circle-notch:before{content:"\f1ce"}.fa-circle-pause:before,.fa-pause-circle:before{content:"\f28b"}.fa-circle-play:before,.fa-play-circle:before{content:"\f144"}.fa-circle-plus:before,.fa-plus-circle:before{content:"\f055"}.fa-circle-question:before,.fa-question-circle:before{content:"\f059"}.fa-circle-radiation:before,.fa-radiation-alt:before{content:"\f7ba"}.fa-arrow-alt-circle-right:before,.fa-circle-right:before{content:"\f35a"}.fa-circle-stop:before,.fa-stop-circle:before{content:"\f28d"}.fa-arrow-alt-circle-up:before,.fa-circle-up:before{content:"\f35b"}.fa-circle-user:before,.fa-user-circle:before{content:"\f2bd"}.fa-circle-xmark:before,.fa-times-circle:before,.fa-xmark-circle:before{content:"\f057"}.fa-city:before{content:"\f64f"}.fa-clapperboard:before{content:"\e131"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clipboard-question:before{content:"\e4e3"}.fa-clipboard-user:before{content:"\f7f3"}.fa-clock-four:before,.fa-clock:before{content:"\f017"}.fa-clock-rotate-left:before,.fa-history:before{content:"\f1da"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-arrow-down:before,.fa-cloud-download-alt:before,.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-arrow-up:before,.fa-cloud-upload-alt:before,.fa-cloud-upload:before{content:"\f0ee"}.fa-cloud-bolt:before,.fa-thunderstorm:before{content:"\f76c"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-showers-water:before{content:"\e4e4"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-clover:before{content:"\e139"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-code-commit:before{content:"\f386"}.fa-code-compare:before{content:"\e13a"}.fa-code-fork:before{content:"\e13b"}.fa-code-merge:before{content:"\f387"}.fa-code-pull-request:before{content:"\e13c"}.fa-coins:before{content:"\f51e"}.fa-colon-sign:before{content:"\e140"}.fa-comment:before{content:"\f075"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before,.fa-commenting:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comment-sms:before,.fa-sms:before{content:"\f7cd"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compass-drafting:before,.fa-drafting-compass:before{content:"\f568"}.fa-compress:before{content:"\f066"}.fa-computer:before{content:"\e4e5"}.fa-computer-mouse:before,.fa-mouse:before{content:"\f8cc"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-couch:before{content:"\f4b8"}.fa-cow:before{content:"\f6c8"}.fa-credit-card-alt:before,.fa-credit-card:before{content:"\f09d"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before,.fa-crop-simple:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-cruzeiro-sign:before{content:"\e152"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cubes-stacked:before{content:"\e4e6"}.fa-d:before{content:"\44"}.fa-database:before{content:"\f1c0"}.fa-backspace:before,.fa-delete-left:before{content:"\f55a"}.fa-democrat:before{content:"\f747"}.fa-desktop-alt:before,.fa-desktop:before{content:"\f390"}.fa-dharmachakra:before{content:"\f655"}.fa-diagram-next:before{content:"\e476"}.fa-diagram-predecessor:before{content:"\e477"}.fa-diagram-project:before,.fa-project-diagram:before{content:"\f542"}.fa-diagram-successor:before{content:"\e47a"}.fa-diamond:before{content:"\f219"}.fa-diamond-turn-right:before,.fa-directions:before{content:"\f5eb"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-disease:before{content:"\f7fa"}.fa-display:before{content:"\e163"}.fa-divide:before{content:"\f529"}.fa-dna:before{content:"\f471"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before,.fa-dollar:before,.fa-usd:before{content:"\24"}.fa-dolly-box:before,.fa-dolly:before{content:"\f472"}.fa-dong-sign:before{content:"\e169"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dove:before{content:"\f4ba"}.fa-compress-alt:before,.fa-down-left-and-up-right-to-center:before{content:"\f422"}.fa-down-long:before,.fa-long-arrow-alt-down:before{content:"\f309"}.fa-download:before{content:"\f019"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-droplet:before,.fa-tint:before{content:"\f043"}.fa-droplet-slash:before,.fa-tint-slash:before{content:"\f5c7"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-e:before{content:"\45"}.fa-deaf:before,.fa-deafness:before,.fa-ear-deaf:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-assistive-listening-systems:before,.fa-ear-listen:before{content:"\f2a2"}.fa-earth-africa:before,.fa-globe-africa:before{content:"\f57c"}.fa-earth-america:before,.fa-earth-americas:before,.fa-earth:before,.fa-globe-americas:before{content:"\f57d"}.fa-earth-asia:before,.fa-globe-asia:before{content:"\f57e"}.fa-earth-europe:before,.fa-globe-europe:before{content:"\f7a2"}.fa-earth-oceania:before,.fa-globe-oceania:before{content:"\e47b"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elevator:before{content:"\e16d"}.fa-ellipsis-h:before,.fa-ellipsis:before{content:"\f141"}.fa-ellipsis-v:before,.fa-ellipsis-vertical:before{content:"\f142"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-circle-check:before{content:"\e4e8"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelopes-bulk:before,.fa-mail-bulk:before{content:"\f674"}.fa-equals:before{content:"\3d"}.fa-eraser:before{content:"\f12d"}.fa-ethernet:before{content:"\f796"}.fa-eur:before,.fa-euro-sign:before,.fa-euro:before{content:"\f153"}.fa-exclamation:before{content:"\21"}.fa-expand:before{content:"\f065"}.fa-explosion:before{content:"\e4e9"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper-empty:before,.fa-eye-dropper:before,.fa-eyedropper:before{content:"\f1fb"}.fa-eye-low-vision:before,.fa-low-vision:before{content:"\f2a8"}.fa-eye-slash:before{content:"\f070"}.fa-f:before{content:"\46"}.fa-angry:before,.fa-face-angry:before{content:"\f556"}.fa-dizzy:before,.fa-face-dizzy:before{content:"\f567"}.fa-face-flushed:before,.fa-flushed:before{content:"\f579"}.fa-face-frown:before,.fa-frown:before{content:"\f119"}.fa-face-frown-open:before,.fa-frown-open:before{content:"\f57a"}.fa-face-grimace:before,.fa-grimace:before{content:"\f57f"}.fa-face-grin:before,.fa-grin:before{content:"\f580"}.fa-face-grin-beam:before,.fa-grin-beam:before{content:"\f582"}.fa-face-grin-beam-sweat:before,.fa-grin-beam-sweat:before{content:"\f583"}.fa-face-grin-hearts:before,.fa-grin-hearts:before{content:"\f584"}.fa-face-grin-squint:before,.fa-grin-squint:before{content:"\f585"}.fa-face-grin-squint-tears:before,.fa-grin-squint-tears:before{content:"\f586"}.fa-face-grin-stars:before,.fa-grin-stars:before{content:"\f587"}.fa-face-grin-tears:before,.fa-grin-tears:before{content:"\f588"}.fa-face-grin-tongue:before,.fa-grin-tongue:before{content:"\f589"}.fa-face-grin-tongue-squint:before,.fa-grin-tongue-squint:before{content:"\f58a"}.fa-face-grin-tongue-wink:before,.fa-grin-tongue-wink:before{content:"\f58b"}.fa-face-grin-wide:before,.fa-grin-alt:before{content:"\f581"}.fa-face-grin-wink:before,.fa-grin-wink:before{content:"\f58c"}.fa-face-kiss:before,.fa-kiss:before{content:"\f596"}.fa-face-kiss-beam:before,.fa-kiss-beam:before{content:"\f597"}.fa-face-kiss-wink-heart:before,.fa-kiss-wink-heart:before{content:"\f598"}.fa-face-laugh:before,.fa-laugh:before{content:"\f599"}.fa-face-laugh-beam:before,.fa-laugh-beam:before{content:"\f59a"}.fa-face-laugh-squint:before,.fa-laugh-squint:before{content:"\f59b"}.fa-face-laugh-wink:before,.fa-laugh-wink:before{content:"\f59c"}.fa-face-meh:before,.fa-meh:before{content:"\f11a"}.fa-face-meh-blank:before,.fa-meh-blank:before{content:"\f5a4"}.fa-face-rolling-eyes:before,.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-face-sad-cry:before,.fa-sad-cry:before{content:"\f5b3"}.fa-face-sad-tear:before,.fa-sad-tear:before{content:"\f5b4"}.fa-face-smile:before,.fa-smile:before{content:"\f118"}.fa-face-smile-beam:before,.fa-smile-beam:before{content:"\f5b8"}.fa-face-smile-wink:before,.fa-smile-wink:before{content:"\f4da"}.fa-face-surprise:before,.fa-surprise:before{content:"\f5c2"}.fa-face-tired:before,.fa-tired:before{content:"\f5c8"}.fa-fan:before{content:"\f863"}.fa-faucet:before{content:"\e005"}.fa-faucet-drip:before{content:"\e006"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before,.fa-feather-pointed:before{content:"\f56b"}.fa-ferry:before{content:"\e4ea"}.fa-file:before{content:"\f15b"}.fa-file-arrow-down:before,.fa-file-download:before{content:"\f56d"}.fa-file-arrow-up:before,.fa-file-upload:before{content:"\f574"}.fa-file-audio:before{content:"\f1c7"}.fa-file-circle-check:before{content:"\e5a0"}.fa-file-circle-exclamation:before{content:"\e4eb"}.fa-file-circle-minus:before{content:"\e4ed"}.fa-file-circle-plus:before{content:"\e494"}.fa-file-circle-question:before{content:"\e4ef"}.fa-file-circle-xmark:before{content:"\e5a1"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-excel:before{content:"\f1c3"}.fa-arrow-right-from-file:before,.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-arrow-right-to-file:before,.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-alt:before,.fa-file-lines:before,.fa-file-text:before{content:"\f15c"}.fa-file-medical:before{content:"\f477"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-edit:before,.fa-file-pen:before{content:"\f31c"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-shield:before{content:"\e4f0"}.fa-file-signature:before{content:"\f573"}.fa-file-video:before{content:"\f1c8"}.fa-file-medical-alt:before,.fa-file-waveform:before{content:"\f478"}.fa-file-word:before{content:"\f1c2"}.fa-file-archive:before,.fa-file-zipper:before{content:"\f1c6"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-filter-circle-dollar:before,.fa-funnel-dollar:before{content:"\f662"}.fa-filter-circle-xmark:before{content:"\e17b"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-burner:before{content:"\e4f1"}.fa-fire-extinguisher:before{content:"\f134"}.fa-fire-alt:before,.fa-fire-flame-curved:before{content:"\f7e4"}.fa-burn:before,.fa-fire-flame-simple:before{content:"\f46a"}.fa-fish:before{content:"\f578"}.fa-fish-fins:before{content:"\e4f2"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flask-vial:before{content:"\e4f3"}.fa-floppy-disk:before,.fa-save:before{content:"\f0c7"}.fa-florin-sign:before{content:"\e184"}.fa-folder-blank:before,.fa-folder:before{content:"\f07b"}.fa-folder-closed:before{content:"\e185"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-folder-tree:before{content:"\f802"}.fa-font:before{content:"\f031"}.fa-football-ball:before,.fa-football:before{content:"\f44e"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before,.fa-forward-fast:before{content:"\f050"}.fa-forward-step:before,.fa-step-forward:before{content:"\f051"}.fa-franc-sign:before{content:"\e18f"}.fa-frog:before{content:"\f52e"}.fa-futbol-ball:before,.fa-futbol:before,.fa-soccer-ball:before{content:"\f1e3"}.fa-g:before{content:"\47"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-dashboard:before,.fa-gauge-med:before,.fa-gauge:before,.fa-tachometer-alt-average:before{content:"\f624"}.fa-gauge-high:before,.fa-tachometer-alt-fast:before,.fa-tachometer-alt:before{content:"\f625"}.fa-gauge-simple-med:before,.fa-gauge-simple:before,.fa-tachometer-average:before{content:"\f629"}.fa-gauge-simple-high:before,.fa-tachometer-fast:before,.fa-tachometer:before{content:"\f62a"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-glass-water:before{content:"\e4f4"}.fa-glass-water-droplet:before{content:"\e4f5"}.fa-glasses:before{content:"\f530"}.fa-globe:before{content:"\f0ac"}.fa-golf-ball-tee:before,.fa-golf-ball:before{content:"\f450"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-greater-than:before{content:"\3e"}.fa-greater-than-equal:before{content:"\f532"}.fa-grip-horizontal:before,.fa-grip:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-group-arrows-rotate:before{content:"\e4f6"}.fa-guarani-sign:before{content:"\e19a"}.fa-guitar:before{content:"\f7a6"}.fa-gun:before{content:"\e19b"}.fa-h:before{content:"\48"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-paper:before,.fa-hand:before{content:"\f256"}.fa-hand-back-fist:before,.fa-hand-rock:before{content:"\f255"}.fa-allergies:before,.fa-hand-dots:before{content:"\f461"}.fa-fist-raised:before,.fa-hand-fist:before{content:"\f6de"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-dollar:before,.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-holding-droplet:before,.fa-hand-holding-water:before{content:"\f4c1"}.fa-hand-holding-hand:before{content:"\e4f7"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-sparkles:before{content:"\e05d"}.fa-hand-spock:before{content:"\f259"}.fa-handcuffs:before{content:"\e4f8"}.fa-hands:before,.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before,.fa-hands-american-sign-language-interpreting:before,.fa-hands-asl-interpreting:before{content:"\f2a3"}.fa-hands-bound:before{content:"\e4f9"}.fa-hands-bubbles:before,.fa-hands-wash:before{content:"\e05e"}.fa-hands-clapping:before{content:"\e1a8"}.fa-hands-holding:before{content:"\f4c2"}.fa-hands-holding-child:before{content:"\e4fa"}.fa-hands-holding-circle:before{content:"\e4fb"}.fa-hands-praying:before,.fa-praying-hands:before{content:"\f684"}.fa-handshake:before{content:"\f2b5"}.fa-hands-helping:before,.fa-handshake-angle:before{content:"\f4c4"}.fa-handshake-alt:before,.fa-handshake-simple:before{content:"\f4c6"}.fa-handshake-alt-slash:before,.fa-handshake-simple-slash:before{content:"\e05f"}.fa-handshake-slash:before{content:"\e060"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-drive:before,.fa-hdd:before{content:"\f0a0"}.fa-hashtag:before{content:"\23"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-hat-wizard:before{content:"\f6e8"}.fa-head-side-cough:before{content:"\e061"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-head-side-mask:before{content:"\e063"}.fa-head-side-virus:before{content:"\e064"}.fa-header:before,.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before,.fa-headphones-simple:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-circle-bolt:before{content:"\e4fc"}.fa-heart-circle-check:before{content:"\e4fd"}.fa-heart-circle-exclamation:before{content:"\e4fe"}.fa-heart-circle-minus:before{content:"\e4ff"}.fa-heart-circle-plus:before{content:"\e500"}.fa-heart-circle-xmark:before{content:"\e501"}.fa-heart-broken:before,.fa-heart-crack:before{content:"\f7a9"}.fa-heart-pulse:before,.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-helicopter-symbol:before{content:"\e502"}.fa-hard-hat:before,.fa-hat-hard:before,.fa-helmet-safety:before{content:"\f807"}.fa-helmet-un:before{content:"\e503"}.fa-highlighter:before{content:"\f591"}.fa-hill-avalanche:before{content:"\e507"}.fa-hill-rockslide:before{content:"\e508"}.fa-hippo:before{content:"\f6ed"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital-alt:before,.fa-hospital-wide:before,.fa-hospital:before{content:"\f0f8"}.fa-hospital-user:before{content:"\f80d"}.fa-hot-tub-person:before,.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hourglass-empty:before,.fa-hourglass:before{content:"\f254"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-home-alt:before,.fa-home-lg-alt:before,.fa-home:before,.fa-house:before{content:"\f015"}.fa-home-lg:before,.fa-house-chimney:before{content:"\e3af"}.fa-house-chimney-crack:before,.fa-house-damage:before{content:"\f6f1"}.fa-clinic-medical:before,.fa-house-chimney-medical:before{content:"\f7f2"}.fa-house-chimney-user:before{content:"\e065"}.fa-house-chimney-window:before{content:"\e00d"}.fa-house-circle-check:before{content:"\e509"}.fa-house-circle-exclamation:before{content:"\e50a"}.fa-house-circle-xmark:before{content:"\e50b"}.fa-house-crack:before{content:"\e3b1"}.fa-house-fire:before{content:"\e50c"}.fa-house-flag:before{content:"\e50d"}.fa-house-flood-water:before{content:"\e50e"}.fa-house-flood-water-circle-arrow-right:before{content:"\e50f"}.fa-house-laptop:before,.fa-laptop-house:before{content:"\e066"}.fa-house-lock:before{content:"\e510"}.fa-house-medical:before{content:"\e3b2"}.fa-house-medical-circle-check:before{content:"\e511"}.fa-house-medical-circle-exclamation:before{content:"\e512"}.fa-house-medical-circle-xmark:before{content:"\e513"}.fa-house-medical-flag:before{content:"\e514"}.fa-house-signal:before{content:"\e012"}.fa-house-tsunami:before{content:"\e515"}.fa-home-user:before,.fa-house-user:before{content:"\e1b0"}.fa-hryvnia-sign:before,.fa-hryvnia:before{content:"\f6f2"}.fa-hurricane:before{content:"\f751"}.fa-i:before{content:"\49"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-heart-music-camera-bolt:before,.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before,.fa-id-card-clip:before{content:"\f47f"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-image-portrait:before,.fa-portrait:before{content:"\f3e0"}.fa-images:before{content:"\f302"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-indian-rupee-sign:before,.fa-indian-rupee:before,.fa-inr:before{content:"\e1bc"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-italic:before{content:"\f033"}.fa-j:before{content:"\4a"}.fa-jar:before{content:"\e516"}.fa-jar-wheat:before{content:"\e517"}.fa-jedi:before{content:"\f669"}.fa-fighter-jet:before,.fa-jet-fighter:before{content:"\f0fb"}.fa-jet-fighter-up:before{content:"\e518"}.fa-joint:before{content:"\f595"}.fa-jug-detergent:before{content:"\e519"}.fa-k:before{content:"\4b"}.fa-kaaba:before{content:"\f66b"}.fa-key:before{content:"\f084"}.fa-keyboard:before{content:"\f11c"}.fa-khanda:before{content:"\f66d"}.fa-kip-sign:before{content:"\e1c4"}.fa-first-aid:before,.fa-kit-medical:before{content:"\f479"}.fa-kitchen-set:before{content:"\e51a"}.fa-kiwi-bird:before{content:"\f535"}.fa-l:before{content:"\4c"}.fa-land-mine-on:before{content:"\e51b"}.fa-landmark:before{content:"\f66f"}.fa-landmark-alt:before,.fa-landmark-dome:before{content:"\f752"}.fa-landmark-flag:before{content:"\e51c"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-file:before{content:"\e51d"}.fa-laptop-medical:before{content:"\f812"}.fa-lari-sign:before{content:"\e1c8"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-left-long:before,.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-arrows-alt-h:before,.fa-left-right:before{content:"\f337"}.fa-lemon:before{content:"\f094"}.fa-less-than:before{content:"\3c"}.fa-less-than-equal:before{content:"\f537"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-lines-leaning:before{content:"\e51e"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-chain-broken:before,.fa-chain-slash:before,.fa-link-slash:before,.fa-unlink:before{content:"\f127"}.fa-lira-sign:before{content:"\f195"}.fa-list-squares:before,.fa-list:before{content:"\f03a"}.fa-list-check:before,.fa-tasks:before{content:"\f0ae"}.fa-list-1-2:before,.fa-list-numeric:before,.fa-list-ol:before{content:"\f0cb"}.fa-list-dots:before,.fa-list-ul:before{content:"\f0ca"}.fa-litecoin-sign:before{content:"\e1d3"}.fa-location-arrow:before{content:"\f124"}.fa-location-crosshairs:before,.fa-location:before{content:"\f601"}.fa-location-dot:before,.fa-map-marker-alt:before{content:"\f3c5"}.fa-location-pin:before,.fa-map-marker:before{content:"\f041"}.fa-location-pin-lock:before{content:"\e51f"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-locust:before{content:"\e520"}.fa-lungs:before{content:"\f604"}.fa-lungs-virus:before{content:"\e067"}.fa-m:before{content:"\4d"}.fa-magnet:before{content:"\f076"}.fa-magnifying-glass:before,.fa-search:before{content:"\f002"}.fa-magnifying-glass-arrow-right:before{content:"\e521"}.fa-magnifying-glass-chart:before{content:"\e522"}.fa-magnifying-glass-dollar:before,.fa-search-dollar:before{content:"\f688"}.fa-magnifying-glass-location:before,.fa-search-location:before{content:"\f689"}.fa-magnifying-glass-minus:before,.fa-search-minus:before{content:"\f010"}.fa-magnifying-glass-plus:before,.fa-search-plus:before{content:"\f00e"}.fa-manat-sign:before{content:"\e1d5"}.fa-map:before{content:"\f279"}.fa-map-location:before,.fa-map-marked:before{content:"\f59f"}.fa-map-location-dot:before,.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-pin:before{content:"\f276"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-and-venus:before{content:"\f224"}.fa-mars-and-venus-burst:before{content:"\e523"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before,.fa-mars-stroke-right:before{content:"\f22b"}.fa-mars-stroke-up:before,.fa-mars-stroke-v:before{content:"\f22a"}.fa-glass-martini-alt:before,.fa-martini-glass:before{content:"\f57b"}.fa-cocktail:before,.fa-martini-glass-citrus:before{content:"\f561"}.fa-glass-martini:before,.fa-martini-glass-empty:before{content:"\f000"}.fa-mask:before{content:"\f6fa"}.fa-mask-face:before{content:"\e1d7"}.fa-mask-ventilator:before{content:"\e524"}.fa-masks-theater:before,.fa-theater-masks:before{content:"\f630"}.fa-mattress-pillow:before{content:"\e525"}.fa-expand-arrows-alt:before,.fa-maximize:before{content:"\f31e"}.fa-medal:before{content:"\f5a2"}.fa-memory:before{content:"\f538"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-comment-alt:before,.fa-message:before{content:"\f27a"}.fa-meteor:before{content:"\f753"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before,.fa-microphone-lines:before{content:"\f3c9"}.fa-microphone-alt-slash:before,.fa-microphone-lines-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-mill-sign:before{content:"\e1ed"}.fa-compress-arrows-alt:before,.fa-minimize:before{content:"\f78c"}.fa-minus:before,.fa-subtract:before{content:"\f068"}.fa-mitten:before{content:"\f7b5"}.fa-mobile-android:before,.fa-mobile-phone:before,.fa-mobile:before{content:"\f3ce"}.fa-mobile-button:before{content:"\f10b"}.fa-mobile-retro:before{content:"\e527"}.fa-mobile-android-alt:before,.fa-mobile-screen:before{content:"\f3cf"}.fa-mobile-alt:before,.fa-mobile-screen-button:before{content:"\f3cd"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-1:before,.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-1-wave:before,.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-bill-transfer:before{content:"\e528"}.fa-money-bill-trend-up:before{content:"\e529"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wheat:before{content:"\e52a"}.fa-money-bills:before{content:"\e1f3"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before,.fa-money-check-dollar:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-mosquito:before{content:"\e52b"}.fa-mosquito-net:before{content:"\e52c"}.fa-motorcycle:before{content:"\f21c"}.fa-mound:before{content:"\e52d"}.fa-mountain:before{content:"\f6fc"}.fa-mountain-city:before{content:"\e52e"}.fa-mountain-sun:before{content:"\e52f"}.fa-mug-hot:before{content:"\f7b6"}.fa-coffee:before,.fa-mug-saucer:before{content:"\f0f4"}.fa-music:before{content:"\f001"}.fa-n:before{content:"\4e"}.fa-naira-sign:before{content:"\e1f6"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-not-equal:before{content:"\f53e"}.fa-notdef:before{content:"\e1fe"}.fa-note-sticky:before,.fa-sticky-note:before{content:"\f249"}.fa-notes-medical:before{content:"\f481"}.fa-o:before{content:"\4f"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-oil-can:before{content:"\f613"}.fa-oil-well:before{content:"\e532"}.fa-om:before{content:"\f679"}.fa-otter:before{content:"\f700"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-p:before{content:"\50"}.fa-pager:before{content:"\f815"}.fa-paint-roller:before{content:"\f5aa"}.fa-paint-brush:before,.fa-paintbrush:before{content:"\f1fc"}.fa-palette:before{content:"\f53f"}.fa-pallet:before{content:"\f482"}.fa-panorama:before{content:"\e209"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-passport:before{content:"\f5ab"}.fa-file-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-pause:before{content:"\f04c"}.fa-paw:before{content:"\f1b0"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before,.fa-pen-clip:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-ruler:before,.fa-pencil-ruler:before{content:"\f5ae"}.fa-edit:before,.fa-pen-to-square:before{content:"\f044"}.fa-pencil-alt:before,.fa-pencil:before{content:"\f303"}.fa-people-arrows-left-right:before,.fa-people-arrows:before{content:"\e068"}.fa-people-carry-box:before,.fa-people-carry:before{content:"\f4ce"}.fa-people-group:before{content:"\e533"}.fa-people-line:before{content:"\e534"}.fa-people-pulling:before{content:"\e535"}.fa-people-robbery:before{content:"\e536"}.fa-people-roof:before{content:"\e537"}.fa-pepper-hot:before{content:"\f816"}.fa-percent:before,.fa-percentage:before{content:"\25"}.fa-male:before,.fa-person:before{content:"\f183"}.fa-person-arrow-down-to-line:before{content:"\e538"}.fa-person-arrow-up-from-line:before{content:"\e539"}.fa-biking:before,.fa-person-biking:before{content:"\f84a"}.fa-person-booth:before{content:"\f756"}.fa-person-breastfeeding:before{content:"\e53a"}.fa-person-burst:before{content:"\e53b"}.fa-person-cane:before{content:"\e53c"}.fa-person-chalkboard:before{content:"\e53d"}.fa-person-circle-check:before{content:"\e53e"}.fa-person-circle-exclamation:before{content:"\e53f"}.fa-person-circle-minus:before{content:"\e540"}.fa-person-circle-plus:before{content:"\e541"}.fa-person-circle-question:before{content:"\e542"}.fa-person-circle-xmark:before{content:"\e543"}.fa-digging:before,.fa-person-digging:before{content:"\f85e"}.fa-diagnoses:before,.fa-person-dots-from-line:before{content:"\f470"}.fa-female:before,.fa-person-dress:before{content:"\f182"}.fa-person-dress-burst:before{content:"\e544"}.fa-person-drowning:before{content:"\e545"}.fa-person-falling:before{content:"\e546"}.fa-person-falling-burst:before{content:"\e547"}.fa-person-half-dress:before{content:"\e548"}.fa-person-harassing:before{content:"\e549"}.fa-hiking:before,.fa-person-hiking:before{content:"\f6ec"}.fa-person-military-pointing:before{content:"\e54a"}.fa-person-military-rifle:before{content:"\e54b"}.fa-person-military-to-person:before{content:"\e54c"}.fa-person-praying:before,.fa-pray:before{content:"\f683"}.fa-person-pregnant:before{content:"\e31e"}.fa-person-rays:before{content:"\e54d"}.fa-person-rifle:before{content:"\e54e"}.fa-person-running:before,.fa-running:before{content:"\f70c"}.fa-person-shelter:before{content:"\e54f"}.fa-person-skating:before,.fa-skating:before{content:"\f7c5"}.fa-person-skiing:before,.fa-skiing:before{content:"\f7c9"}.fa-person-skiing-nordic:before,.fa-skiing-nordic:before{content:"\f7ca"}.fa-person-snowboarding:before,.fa-snowboarding:before{content:"\f7ce"}.fa-person-swimming:before,.fa-swimmer:before{content:"\f5c4"}.fa-person-through-window:before{content:"\e5a9"}.fa-person-walking:before,.fa-walking:before{content:"\f554"}.fa-person-walking-arrow-loop-left:before{content:"\e551"}.fa-person-walking-arrow-right:before{content:"\e552"}.fa-person-walking-dashed-line-arrow-right:before{content:"\e553"}.fa-person-walking-luggage:before{content:"\e554"}.fa-blind:before,.fa-person-walking-with-cane:before{content:"\f29d"}.fa-peseta-sign:before{content:"\e221"}.fa-peso-sign:before{content:"\e222"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before,.fa-phone-flip:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-volume:before,.fa-volume-control-phone:before{content:"\f2a0"}.fa-photo-film:before,.fa-photo-video:before{content:"\f87c"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-circle-check:before{content:"\e555"}.fa-plane-circle-exclamation:before{content:"\e556"}.fa-plane-circle-xmark:before{content:"\e557"}.fa-plane-departure:before{content:"\f5b0"}.fa-plane-lock:before{content:"\e558"}.fa-plane-slash:before{content:"\e069"}.fa-plane-up:before{content:"\e22d"}.fa-plant-wilt:before{content:"\e5aa"}.fa-plate-wheat:before{content:"\e55a"}.fa-play:before{content:"\f04b"}.fa-plug:before{content:"\f1e6"}.fa-plug-circle-bolt:before{content:"\e55b"}.fa-plug-circle-check:before{content:"\e55c"}.fa-plug-circle-exclamation:before{content:"\e55d"}.fa-plug-circle-minus:before{content:"\e55e"}.fa-plug-circle-plus:before{content:"\e55f"}.fa-plug-circle-xmark:before{content:"\e560"}.fa-add:before,.fa-plus:before{content:"\2b"}.fa-plus-minus:before{content:"\e43c"}.fa-podcast:before{content:"\f2ce"}.fa-poo:before{content:"\f2fe"}.fa-poo-bolt:before,.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-power-off:before{content:"\f011"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before,.fa-prescription-bottle-medical:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-pump-medical:before{content:"\e06a"}.fa-pump-soap:before{content:"\e06b"}.fa-puzzle-piece:before{content:"\f12e"}.fa-q:before{content:"\51"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\3f"}.fa-quote-left-alt:before,.fa-quote-left:before{content:"\f10d"}.fa-quote-right-alt:before,.fa-quote-right:before{content:"\f10e"}.fa-r:before{content:"\52"}.fa-radiation:before{content:"\f7b9"}.fa-radio:before{content:"\f8d7"}.fa-rainbow:before{content:"\f75b"}.fa-ranking-star:before{content:"\e561"}.fa-receipt:before{content:"\f543"}.fa-record-vinyl:before{content:"\f8d9"}.fa-ad:before,.fa-rectangle-ad:before{content:"\f641"}.fa-list-alt:before,.fa-rectangle-list:before{content:"\f022"}.fa-rectangle-times:before,.fa-rectangle-xmark:before,.fa-times-rectangle:before,.fa-window-close:before{content:"\f410"}.fa-recycle:before{content:"\f1b8"}.fa-registered:before{content:"\f25d"}.fa-repeat:before{content:"\f363"}.fa-mail-reply:before,.fa-reply:before{content:"\f3e5"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-republican:before{content:"\f75e"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-ribbon:before{content:"\f4d6"}.fa-right-from-bracket:before,.fa-sign-out-alt:before{content:"\f2f5"}.fa-exchange-alt:before,.fa-right-left:before{content:"\f362"}.fa-long-arrow-alt-right:before,.fa-right-long:before{content:"\f30b"}.fa-right-to-bracket:before,.fa-sign-in-alt:before{content:"\f2f6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-road-barrier:before{content:"\e562"}.fa-road-bridge:before{content:"\e563"}.fa-road-circle-check:before{content:"\e564"}.fa-road-circle-exclamation:before{content:"\e565"}.fa-road-circle-xmark:before{content:"\e566"}.fa-road-lock:before{content:"\e567"}.fa-road-spikes:before{content:"\e568"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rotate:before,.fa-sync-alt:before{content:"\f2f1"}.fa-rotate-back:before,.fa-rotate-backward:before,.fa-rotate-left:before,.fa-undo-alt:before{content:"\f2ea"}.fa-redo-alt:before,.fa-rotate-forward:before,.fa-rotate-right:before{content:"\f2f9"}.fa-route:before{content:"\f4d7"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-rouble:before,.fa-rub:before,.fa-ruble-sign:before,.fa-ruble:before{content:"\f158"}.fa-rug:before{content:"\e569"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-rupee-sign:before,.fa-rupee:before{content:"\f156"}.fa-rupiah-sign:before{content:"\e23d"}.fa-s:before{content:"\53"}.fa-sack-dollar:before{content:"\f81d"}.fa-sack-xmark:before{content:"\e56a"}.fa-sailboat:before{content:"\e445"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-balance-scale:before,.fa-scale-balanced:before{content:"\f24e"}.fa-balance-scale-left:before,.fa-scale-unbalanced:before{content:"\f515"}.fa-balance-scale-right:before,.fa-scale-unbalanced-flip:before{content:"\f516"}.fa-school:before{content:"\f549"}.fa-school-circle-check:before{content:"\e56b"}.fa-school-circle-exclamation:before{content:"\e56c"}.fa-school-circle-xmark:before{content:"\e56d"}.fa-school-flag:before{content:"\e56e"}.fa-school-lock:before{content:"\e56f"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-screwdriver:before{content:"\f54a"}.fa-screwdriver-wrench:before,.fa-tools:before{content:"\f7d9"}.fa-scroll:before{content:"\f70e"}.fa-scroll-torah:before,.fa-torah:before{content:"\f6a0"}.fa-sd-card:before{content:"\f7c2"}.fa-section:before{content:"\e447"}.fa-seedling:before,.fa-sprout:before{content:"\f4d8"}.fa-server:before{content:"\f233"}.fa-shapes:before,.fa-triangle-circle-square:before{content:"\f61f"}.fa-arrow-turn-right:before,.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-share-from-square:before,.fa-share-square:before{content:"\f14d"}.fa-share-alt:before,.fa-share-nodes:before{content:"\f1e0"}.fa-sheet-plastic:before{content:"\e571"}.fa-ils:before,.fa-shekel-sign:before,.fa-shekel:before,.fa-sheqel-sign:before,.fa-sheqel:before{content:"\f20b"}.fa-shield-blank:before,.fa-shield:before{content:"\f132"}.fa-shield-cat:before{content:"\e572"}.fa-shield-dog:before{content:"\e573"}.fa-shield-alt:before,.fa-shield-halved:before{content:"\f3ed"}.fa-shield-heart:before{content:"\e574"}.fa-shield-virus:before{content:"\e06c"}.fa-ship:before{content:"\f21a"}.fa-shirt:before,.fa-t-shirt:before,.fa-tshirt:before{content:"\f553"}.fa-shoe-prints:before{content:"\f54b"}.fa-shop:before,.fa-store-alt:before{content:"\f54f"}.fa-shop-lock:before{content:"\e4a5"}.fa-shop-slash:before,.fa-store-alt-slash:before{content:"\e070"}.fa-shower:before{content:"\f2cc"}.fa-shrimp:before{content:"\e448"}.fa-random:before,.fa-shuffle:before{content:"\f074"}.fa-shuttle-space:before,.fa-space-shuttle:before{content:"\f197"}.fa-sign-hanging:before,.fa-sign:before{content:"\f4d9"}.fa-signal-5:before,.fa-signal-perfect:before,.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-map-signs:before,.fa-signs-post:before{content:"\f277"}.fa-sim-card:before{content:"\f7c4"}.fa-sink:before{content:"\e06d"}.fa-sitemap:before{content:"\f0e8"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before,.fa-sliders:before{content:"\f1de"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-soap:before{content:"\e06e"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-spa:before{content:"\f5bb"}.fa-pastafarianism:before,.fa-spaghetti-monster-flying:before{content:"\f67b"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spoon:before,.fa-utensil-spoon:before{content:"\f2e5"}.fa-spray-can:before{content:"\f5bd"}.fa-air-freshener:before,.fa-spray-can-sparkles:before{content:"\f5d0"}.fa-square:before{content:"\f0c8"}.fa-external-link-square:before,.fa-square-arrow-up-right:before{content:"\f14c"}.fa-caret-square-down:before,.fa-square-caret-down:before{content:"\f150"}.fa-caret-square-left:before,.fa-square-caret-left:before{content:"\f191"}.fa-caret-square-right:before,.fa-square-caret-right:before{content:"\f152"}.fa-caret-square-up:before,.fa-square-caret-up:before{content:"\f151"}.fa-check-square:before,.fa-square-check:before{content:"\f14a"}.fa-envelope-square:before,.fa-square-envelope:before{content:"\f199"}.fa-square-full:before{content:"\f45c"}.fa-h-square:before,.fa-square-h:before{content:"\f0fd"}.fa-minus-square:before,.fa-square-minus:before{content:"\f146"}.fa-square-nfi:before{content:"\e576"}.fa-parking:before,.fa-square-parking:before{content:"\f540"}.fa-pen-square:before,.fa-pencil-square:before,.fa-square-pen:before{content:"\f14b"}.fa-square-person-confined:before{content:"\e577"}.fa-phone-square:before,.fa-square-phone:before{content:"\f098"}.fa-phone-square-alt:before,.fa-square-phone-flip:before{content:"\f87b"}.fa-plus-square:before,.fa-square-plus:before{content:"\f0fe"}.fa-poll-h:before,.fa-square-poll-horizontal:before{content:"\f682"}.fa-poll:before,.fa-square-poll-vertical:before{content:"\f681"}.fa-square-root-alt:before,.fa-square-root-variable:before{content:"\f698"}.fa-rss-square:before,.fa-square-rss:before{content:"\f143"}.fa-share-alt-square:before,.fa-square-share-nodes:before{content:"\f1e1"}.fa-external-link-square-alt:before,.fa-square-up-right:before{content:"\f360"}.fa-square-virus:before{content:"\e578"}.fa-square-xmark:before,.fa-times-square:before,.fa-xmark-square:before{content:"\f2d3"}.fa-rod-asclepius:before,.fa-rod-snake:before,.fa-staff-aesculapius:before,.fa-staff-snake:before{content:"\e579"}.fa-stairs:before{content:"\e289"}.fa-stamp:before{content:"\f5bf"}.fa-stapler:before{content:"\e5af"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before,.fa-star-half-stroke:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-gbp:before,.fa-pound-sign:before,.fa-sterling-sign:before{content:"\f154"}.fa-stethoscope:before{content:"\f0f1"}.fa-stop:before{content:"\f04d"}.fa-stopwatch:before{content:"\f2f2"}.fa-stopwatch-20:before{content:"\e06f"}.fa-store:before{content:"\f54e"}.fa-store-slash:before{content:"\e071"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stroopwafel:before{content:"\f551"}.fa-subscript:before{content:"\f12c"}.fa-suitcase:before{content:"\f0f2"}.fa-medkit:before,.fa-suitcase-medical:before{content:"\f0fa"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-sun-plant-wilt:before{content:"\e57a"}.fa-superscript:before{content:"\f12b"}.fa-swatchbook:before{content:"\f5c3"}.fa-synagogue:before{content:"\f69b"}.fa-syringe:before{content:"\f48e"}.fa-t:before{content:"\54"}.fa-table:before{content:"\f0ce"}.fa-table-cells:before,.fa-th:before{content:"\f00a"}.fa-table-cells-large:before,.fa-th-large:before{content:"\f009"}.fa-columns:before,.fa-table-columns:before{content:"\f0db"}.fa-table-list:before,.fa-th-list:before{content:"\f00b"}.fa-ping-pong-paddle-ball:before,.fa-table-tennis-paddle-ball:before,.fa-table-tennis:before{content:"\f45d"}.fa-tablet-android:before,.fa-tablet:before{content:"\f3fb"}.fa-tablet-button:before{content:"\f10a"}.fa-tablet-alt:before,.fa-tablet-screen-button:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-digital-tachograph:before,.fa-tachograph-digital:before{content:"\f566"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tarp:before{content:"\e57b"}.fa-tarp-droplet:before{content:"\e57c"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-temperature-arrow-down:before,.fa-temperature-down:before{content:"\e03f"}.fa-temperature-arrow-up:before,.fa-temperature-up:before{content:"\e040"}.fa-temperature-0:before,.fa-temperature-empty:before,.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-temperature-4:before,.fa-temperature-full:before,.fa-thermometer-4:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-temperature-2:before,.fa-temperature-half:before,.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-temperature-1:before,.fa-temperature-quarter:before,.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-temperature-3:before,.fa-temperature-three-quarters:before,.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-tenge-sign:before,.fa-tenge:before{content:"\f7d7"}.fa-tent:before{content:"\e57d"}.fa-tent-arrow-down-to-line:before{content:"\e57e"}.fa-tent-arrow-left-right:before{content:"\e57f"}.fa-tent-arrow-turn-left:before{content:"\e580"}.fa-tent-arrows-down:before{content:"\e581"}.fa-tents:before{content:"\e582"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-remove-format:before,.fa-text-slash:before{content:"\f87d"}.fa-text-width:before{content:"\f035"}.fa-thermometer:before{content:"\f491"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumb-tack:before,.fa-thumbtack:before{content:"\f08d"}.fa-ticket:before{content:"\f145"}.fa-ticket-alt:before,.fa-ticket-simple:before{content:"\f3ff"}.fa-timeline:before{content:"\e29c"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-toilet-portable:before{content:"\e583"}.fa-toilets-portable:before{content:"\e584"}.fa-toolbox:before{content:"\f552"}.fa-tooth:before{content:"\f5c9"}.fa-torii-gate:before{content:"\f6a1"}.fa-tornado:before{content:"\f76f"}.fa-broadcast-tower:before,.fa-tower-broadcast:before{content:"\f519"}.fa-tower-cell:before{content:"\e585"}.fa-tower-observation:before{content:"\e586"}.fa-tractor:before{content:"\f722"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-trailer:before{content:"\e041"}.fa-train:before{content:"\f238"}.fa-subway:before,.fa-train-subway:before{content:"\f239"}.fa-train-tram:before{content:"\e5b4"}.fa-transgender-alt:before,.fa-transgender:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-arrow-up:before,.fa-trash-restore:before{content:"\f829"}.fa-trash-alt:before,.fa-trash-can:before{content:"\f2ed"}.fa-trash-can-arrow-up:before,.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-tree-city:before{content:"\e587"}.fa-exclamation-triangle:before,.fa-triangle-exclamation:before,.fa-warning:before{content:"\f071"}.fa-trophy:before{content:"\f091"}.fa-trowel:before{content:"\e589"}.fa-trowel-bricks:before{content:"\e58a"}.fa-truck:before{content:"\f0d1"}.fa-truck-arrow-right:before{content:"\e58b"}.fa-truck-droplet:before{content:"\e58c"}.fa-shipping-fast:before,.fa-truck-fast:before{content:"\f48b"}.fa-truck-field:before{content:"\e58d"}.fa-truck-field-un:before{content:"\e58e"}.fa-truck-front:before{content:"\e2b7"}.fa-ambulance:before,.fa-truck-medical:before{content:"\f0f9"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-truck-plane:before{content:"\e58f"}.fa-truck-loading:before,.fa-truck-ramp-box:before{content:"\f4de"}.fa-teletype:before,.fa-tty:before{content:"\f1e4"}.fa-try:before,.fa-turkish-lira-sign:before,.fa-turkish-lira:before{content:"\e2bb"}.fa-level-down-alt:before,.fa-turn-down:before{content:"\f3be"}.fa-level-up-alt:before,.fa-turn-up:before{content:"\f3bf"}.fa-television:before,.fa-tv-alt:before,.fa-tv:before{content:"\f26c"}.fa-u:before{content:"\55"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-universal-access:before{content:"\f29a"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before,.fa-unlock-keyhole:before{content:"\f13e"}.fa-arrows-alt-v:before,.fa-up-down:before{content:"\f338"}.fa-arrows-alt:before,.fa-up-down-left-right:before{content:"\f0b2"}.fa-long-arrow-alt-up:before,.fa-up-long:before{content:"\f30c"}.fa-expand-alt:before,.fa-up-right-and-down-left-from-center:before{content:"\f424"}.fa-external-link-alt:before,.fa-up-right-from-square:before{content:"\f35d"}.fa-upload:before{content:"\f093"}.fa-user:before{content:"\f007"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-clock:before{content:"\f4fd"}.fa-user-doctor:before,.fa-user-md:before{content:"\f0f0"}.fa-user-cog:before,.fa-user-gear:before{content:"\f4fe"}.fa-user-graduate:before{content:"\f501"}.fa-user-friends:before,.fa-user-group:before{content:"\f500"}.fa-user-injured:before{content:"\f728"}.fa-user-alt:before,.fa-user-large:before{content:"\f406"}.fa-user-alt-slash:before,.fa-user-large-slash:before{content:"\f4fa"}.fa-user-lock:before{content:"\f502"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-edit:before,.fa-user-pen:before{content:"\f4ff"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before,.fa-user-xmark:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-between-lines:before{content:"\e591"}.fa-users-cog:before,.fa-users-gear:before{content:"\f509"}.fa-users-line:before{content:"\e592"}.fa-users-rays:before{content:"\e593"}.fa-users-rectangle:before{content:"\e594"}.fa-users-slash:before{content:"\e073"}.fa-users-viewfinder:before{content:"\e595"}.fa-cutlery:before,.fa-utensils:before{content:"\f2e7"}.fa-v:before{content:"\56"}.fa-shuttle-van:before,.fa-van-shuttle:before{content:"\f5b6"}.fa-vault:before{content:"\e2c5"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-vest:before{content:"\e085"}.fa-vest-patches:before{content:"\e086"}.fa-vial:before{content:"\f492"}.fa-vial-circle-check:before{content:"\e596"}.fa-vial-virus:before{content:"\e597"}.fa-vials:before{content:"\f493"}.fa-video-camera:before,.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-virus:before{content:"\e074"}.fa-virus-covid:before{content:"\e4a8"}.fa-virus-covid-slash:before{content:"\e4a9"}.fa-virus-slash:before{content:"\e075"}.fa-viruses:before{content:"\e076"}.fa-voicemail:before{content:"\f897"}.fa-volcano:before{content:"\f770"}.fa-volleyball-ball:before,.fa-volleyball:before{content:"\f45f"}.fa-volume-high:before,.fa-volume-up:before{content:"\f028"}.fa-volume-down:before,.fa-volume-low:before{content:"\f027"}.fa-volume-off:before{content:"\f026"}.fa-volume-mute:before,.fa-volume-times:before,.fa-volume-xmark:before{content:"\f6a9"}.fa-vr-cardboard:before{content:"\f729"}.fa-w:before{content:"\57"}.fa-walkie-talkie:before{content:"\f8ef"}.fa-wallet:before{content:"\f555"}.fa-magic:before,.fa-wand-magic:before{content:"\f0d0"}.fa-magic-wand-sparkles:before,.fa-wand-magic-sparkles:before{content:"\e2ca"}.fa-wand-sparkles:before{content:"\f72b"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-ladder-water:before,.fa-swimming-pool:before,.fa-water-ladder:before{content:"\f5c5"}.fa-wave-square:before{content:"\f83e"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weight-scale:before,.fa-weight:before{content:"\f496"}.fa-wheat-alt:before,.fa-wheat-awn:before{content:"\e2cd"}.fa-wheat-awn-circle-exclamation:before{content:"\e598"}.fa-wheelchair:before{content:"\f193"}.fa-wheelchair-alt:before,.fa-wheelchair-move:before{content:"\e2ce"}.fa-glass-whiskey:before,.fa-whiskey-glass:before{content:"\f7a0"}.fa-wifi-3:before,.fa-wifi-strong:before,.fa-wifi:before{content:"\f1eb"}.fa-wind:before{content:"\f72e"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before,.fa-wine-glass-empty:before{content:"\f5ce"}.fa-krw:before,.fa-won-sign:before,.fa-won:before{content:"\f159"}.fa-worm:before{content:"\e599"}.fa-wrench:before{content:"\f0ad"}.fa-x:before{content:"\58"}.fa-x-ray:before{content:"\f497"}.fa-close:before,.fa-multiply:before,.fa-remove:before,.fa-times:before,.fa-xmark:before{content:"\f00d"}.fa-xmarks-lines:before{content:"\e59a"}.fa-y:before{content:"\59"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen-sign:before,.fa-yen:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-z:before{content:"\5a"}.fa-sr-only,.fa-sr-only-focusable:not(:focus),.sr-only,.sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}:host,:root{--fa-font-brands:normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}.fa-brands,.fab{font-family:"Font Awesome 6 Brands";font-weight:400}.fa-42-group:before,.fa-innosoft:before{content:"\e080"}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-alipay:before{content:"\f642"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-amilia:before{content:"\f36d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-pay:before{content:"\f415"}.fa-artstation:before{content:"\f77a"}.fa-asymmetrik:before{content:"\f372"}.fa-atlassian:before{content:"\f77b"}.fa-audible:before{content:"\f373"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-aws:before{content:"\f375"}.fa-bandcamp:before{content:"\f2d5"}.fa-battle-net:before{content:"\f835"}.fa-behance:before{content:"\f1b4"}.fa-bilibili:before{content:"\e3d9"}.fa-bimobject:before{content:"\f378"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bootstrap:before{content:"\f836"}.fa-bots:before{content:"\e340"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-buromobelexperte:before{content:"\f37f"}.fa-buy-n-large:before{content:"\f8a6"}.fa-buysellads:before{content:"\f20d"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-cloudflare:before{content:"\e07d"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cmplid:before{content:"\e360"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cotton-bureau:before{content:"\f89e"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-critical-role:before{content:"\f6c9"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dailymotion:before{content:"\e052"}.fa-dashcube:before{content:"\f210"}.fa-deezer:before{content:"\e077"}.fa-delicious:before{content:"\f1a5"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dhl:before{content:"\f790"}.fa-diaspora:before{content:"\f791"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-draft2digital:before{content:"\f396"}.fa-dribbble:before{content:"\f17d"}.fa-dropbox:before{content:"\f16b"}.fa-drupal:before{content:"\f1a9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edge-legacy:before{content:"\e078"}.fa-elementor:before{content:"\f430"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envira:before{content:"\f299"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-etsy:before{content:"\f2d7"}.fa-evernote:before{content:"\f839"}.fa-expeditedssl:before{content:"\f23e"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-figma:before{content:"\f799"}.fa-firefox:before{content:"\f269"}.fa-firefox-browser:before{content:"\e007"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-fly:before{content:"\f417"}.fa-font-awesome-flag:before,.fa-font-awesome-logo-full:before,.fa-font-awesome:before{content:"\f2b4"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-fulcrum:before{content:"\f50b"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-gofore:before{content:"\f3a7"}.fa-golang:before{content:"\e40f"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-pay:before{content:"\e079"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-wallet:before{content:"\f1ee"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guilded:before{content:"\e07e"}.fa-gulp:before{content:"\f3ae"}.fa-hacker-news:before{content:"\f1d4"}.fa-hackerrank:before{content:"\f5f7"}.fa-hashnode:before{content:"\e499"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-hive:before{content:"\e07f"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-hotjar:before{content:"\f3b1"}.fa-houzz:before{content:"\f27c"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-ideal:before{content:"\e013"}.fa-imdb:before{content:"\f2d8"}.fa-instagram:before{content:"\f16d"}.fa-instalod:before{content:"\e081"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joomla:before{content:"\f1aa"}.fa-js:before{content:"\f3b8"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaggle:before{content:"\f5fa"}.fa-keybase:before{content:"\f4f5"}.fa-keycdn:before{content:"\f3ba"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-korvue:before{content:"\f42f"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-leanpub:before{content:"\f212"}.fa-less:before{content:"\f41d"}.fa-line:before{content:"\f3c0"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-mailchimp:before{content:"\f59e"}.fa-mandalorian:before{content:"\f50f"}.fa-markdown:before{content:"\f60f"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-mdb:before{content:"\f8ca"}.fa-medapps:before{content:"\f3c6"}.fa-medium-m:before,.fa-medium:before{content:"\f23a"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-mendeley:before{content:"\f7b3"}.fa-meta:before{content:"\e49b"}.fa-microblog:before{content:"\e01a"}.fa-microsoft:before{content:"\f3ca"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mixer:before{content:"\e056"}.fa-mizuni:before{content:"\f3cc"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-nfc-directional:before{content:"\e530"}.fa-nfc-symbol:before{content:"\e531"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-octopus-deploy:before{content:"\e082"}.fa-odnoklassniki:before{content:"\f263"}.fa-old-republic:before{content:"\f510"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-orcid:before{content:"\f8d2"}.fa-osi:before{content:"\f41a"}.fa-padlet:before{content:"\e4a0"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-palfed:before{content:"\f3d8"}.fa-patreon:before{content:"\f3d9"}.fa-paypal:before{content:"\f1ed"}.fa-perbyte:before{content:"\e083"}.fa-periscope:before{content:"\f3da"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pix:before{content:"\e43a"}.fa-playstation:before{content:"\f3df"}.fa-product-hunt:before{content:"\f288"}.fa-pushed:before{content:"\f3e1"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-r-project:before{content:"\f4f7"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-redhat:before{content:"\f7bc"}.fa-renren:before{content:"\f18b"}.fa-replyd:before{content:"\f3e6"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-rev:before{content:"\f5b2"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-rust:before{content:"\e07a"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-schlix:before{content:"\f3ea"}.fa-screenpal:before{content:"\e570"}.fa-scribd:before{content:"\f28a"}.fa-searchengin:before{content:"\f3eb"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-servicestack:before{content:"\f3ec"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shopify:before{content:"\e057"}.fa-shopware:before{content:"\f5b5"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sith:before{content:"\f512"}.fa-sitrox:before{content:"\e44a"}.fa-sketch:before{content:"\f7c6"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack-hash:before,.fa-slack:before{content:"\f198"}.fa-slideshare:before{content:"\f1e7"}.fa-snapchat-ghost:before,.fa-snapchat:before{content:"\f2ab"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-space-awesome:before{content:"\e5ac"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spotify:before{content:"\f1bc"}.fa-behance-square:before,.fa-square-behance:before{content:"\f1b5"}.fa-dribbble-square:before,.fa-square-dribbble:before{content:"\f397"}.fa-facebook-square:before,.fa-square-facebook:before{content:"\f082"}.fa-square-font-awesome:before{content:"\e5ad"}.fa-font-awesome-alt:before,.fa-square-font-awesome-stroke:before{content:"\f35c"}.fa-git-square:before,.fa-square-git:before{content:"\f1d2"}.fa-github-square:before,.fa-square-github:before{content:"\f092"}.fa-gitlab-square:before,.fa-square-gitlab:before{content:"\e5ae"}.fa-google-plus-square:before,.fa-square-google-plus:before{content:"\f0d4"}.fa-hacker-news-square:before,.fa-square-hacker-news:before{content:"\f3af"}.fa-instagram-square:before,.fa-square-instagram:before{content:"\e055"}.fa-js-square:before,.fa-square-js:before{content:"\f3b9"}.fa-lastfm-square:before,.fa-square-lastfm:before{content:"\f203"}.fa-odnoklassniki-square:before,.fa-square-odnoklassniki:before{content:"\f264"}.fa-pied-piper-square:before,.fa-square-pied-piper:before{content:"\e01e"}.fa-pinterest-square:before,.fa-square-pinterest:before{content:"\f0d3"}.fa-reddit-square:before,.fa-square-reddit:before{content:"\f1a2"}.fa-snapchat-square:before,.fa-square-snapchat:before{content:"\f2ad"}.fa-square-steam:before,.fa-steam-square:before{content:"\f1b7"}.fa-square-tumblr:before,.fa-tumblr-square:before{content:"\f174"}.fa-square-twitter:before,.fa-twitter-square:before{content:"\f081"}.fa-square-viadeo:before,.fa-viadeo-square:before{content:"\f2aa"}.fa-square-vimeo:before,.fa-vimeo-square:before{content:"\f194"}.fa-square-whatsapp:before,.fa-whatsapp-square:before{content:"\f40c"}.fa-square-xing:before,.fa-xing-square:before{content:"\f169"}.fa-square-youtube:before,.fa-youtube-square:before{content:"\f431"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-symbol:before{content:"\f3f6"}.fa-sticker-mule:before{content:"\f3f7"}.fa-strava:before{content:"\f428"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-superpowers:before{content:"\f2dd"}.fa-supple:before{content:"\f3f9"}.fa-suse:before{content:"\f7d6"}.fa-swift:before{content:"\f8e1"}.fa-symfony:before{content:"\f83d"}.fa-teamspeak:before{content:"\f4f9"}.fa-telegram-plane:before,.fa-telegram:before{content:"\f2c6"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-the-red-yeti:before{content:"\f69d"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-think-peaks:before{content:"\f731"}.fa-tiktok:before{content:"\e07b"}.fa-trade-federation:before{content:"\f513"}.fa-trello:before{content:"\f181"}.fa-tumblr:before{content:"\f173"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbraco:before{content:"\f8e8"}.fa-uncharted:before{content:"\e084"}.fa-uniregistry:before{content:"\f404"}.fa-unity:before{content:"\e049"}.fa-unsplash:before{content:"\e07c"}.fa-untappd:before{content:"\f405"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-vaadin:before{content:"\f408"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viber:before{content:"\f409"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-vuejs:before{content:"\f41f"}.fa-watchman-monitoring:before{content:"\e087"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whmcs:before{content:"\f40d"}.fa-wikipedia-w:before{content:"\f266"}.fa-windows:before{content:"\f17a"}.fa-wirsindhandwerk:before,.fa-wsh:before{content:"\e2d0"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wodu:before{content:"\e088"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-rendact:before,.fa-wpressr:before{content:"\f3e4"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-zhihu:before{content:"\f63f"}:host,:root{--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-family:"Font Awesome 6 Free";font-weight:400}:host,:root{--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-family:"Font Awesome 6 Free";font-weight:900}@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2"),url(../webfonts/fa-v4compatibility.ttf) format("truetype");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a} \ No newline at end of file diff --git a/image-scanner/vendor/github.com/arl/statsviz/internal/static/libs/js/bootstrap-toggle.min.js b/image-scanner/vendor/github.com/arl/statsviz/internal/static/libs/js/bootstrap-toggle.min.js new file mode 100644 index 000000000..5bed0ff7e --- /dev/null +++ b/image-scanner/vendor/github.com/arl/statsviz/internal/static/libs/js/bootstrap-toggle.min.js @@ -0,0 +1,8 @@ +/*! ======================================================================== + * Bootstrap Toggle: bootstrap-toggle.js v2.2.0 + * http://www.bootstraptoggle.com + * ======================================================================== + * Copyright 2014 Min Hur, The New York Times Company + * Licensed under MIT + * ======================================================================== */ ++function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.toggle"),f="object"==typeof b&&b;e||d.data("bs.toggle",e=new c(this,f)),"string"==typeof b&&e[b]&&e[b]()})}var c=function(b,c){this.$element=a(b),this.options=a.extend({},this.defaults(),c),this.render()};c.VERSION="2.2.0",c.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"default",size:"normal",style:"",width:null,height:null},c.prototype.defaults=function(){return{on:this.$element.attr("data-on")||c.DEFAULTS.on,off:this.$element.attr("data-off")||c.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||c.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||c.DEFAULTS.offstyle,size:this.$element.attr("data-size")||c.DEFAULTS.size,style:this.$element.attr("data-style")||c.DEFAULTS.style,width:this.$element.attr("data-width")||c.DEFAULTS.width,height:this.$element.attr("data-height")||c.DEFAULTS.height}},c.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var b="large"===this.options.size?"btn-lg":"small"===this.options.size?"btn-sm":"mini"===this.options.size?"btn-xs":"",c=a('