mirror of
https://github.com/safedep/vet.git
synced 2025-12-10 00:22:08 -06:00
feat: Code analysis framework infra
feat: Building code graph Refactor to support import processing Handle relative import name fixup Add docs for code analysis framework Update docs to include additional examples feat: Function call graph Update code graph to link function decl and calls Include call node in function calls feat: Flatten vulnerabilities in CSV reporter refactor: Maintain separation of concerns for code analysis framework refactor: Separate storage entities in its own package feat: Add callback support in code graph builder docs: Fix code analysis framework docs Signed-off-by: abhisek <abhisek.datta@gmail.com>
This commit is contained in:
parent
1645f4003d
commit
e6f6288701
199
code.go
Normal file
199
code.go
Normal file
@ -0,0 +1,199 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/safedep/dry/utils"
|
||||
"github.com/safedep/vet/internal/ui"
|
||||
"github.com/safedep/vet/pkg/code"
|
||||
"github.com/safedep/vet/pkg/code/languages"
|
||||
"github.com/safedep/vet/pkg/common/logger"
|
||||
"github.com/safedep/vet/pkg/storage/graph"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
codeAppDirectories = []string{}
|
||||
codeImportDirectories = []string{}
|
||||
codeGraphDatabase string
|
||||
codeLanguage string
|
||||
)
|
||||
|
||||
func newCodeCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "code",
|
||||
Short: "[EXPERIMENTAL] Perform code analysis with insights data",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringArrayVarP(&codeAppDirectories, "src", "", []string{}, "Source code root directory to analyze")
|
||||
cmd.Flags().StringArrayVarP(&codeImportDirectories, "imports", "", []string{}, "Language specific directory to find imported source")
|
||||
cmd.Flags().StringVarP(&codeGraphDatabase, "db", "", "", "Path to the database")
|
||||
cmd.Flags().StringVarP(&codeLanguage, "lang", "", "python", "Language of the source code")
|
||||
|
||||
err := cmd.MarkFlagRequired("db")
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to mark flag as required: %v", err)
|
||||
}
|
||||
|
||||
cmd.AddCommand(newCodeCreateDatabaseCommand())
|
||||
cmd.AddCommand(newCodeImportReachabilityCommand())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCodeCreateDatabaseCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "create-db",
|
||||
Short: "Analyse code and create a database for further analysis",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
startCreateDatabase()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newCodeImportReachabilityCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "import-reachability",
|
||||
Short: "Analyse import reachability",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
startImportReachability()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func startCreateDatabase() {
|
||||
failOnError("code-create-db", internalStartCreateDatabase())
|
||||
}
|
||||
|
||||
func startImportReachability() {
|
||||
failOnError("code-import-reachability-analysis", internalStartImportReachability())
|
||||
}
|
||||
|
||||
func internalStartImportReachability() error {
|
||||
codePrintExperimentalWarning()
|
||||
|
||||
if utils.IsEmptyString(codeGraphDatabase) {
|
||||
return fmt.Errorf("no database path provided")
|
||||
}
|
||||
|
||||
// TODO: We need a code graph loader to load the code graph from the database
|
||||
// before invoking analysis modules
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func internalStartCreateDatabase() error {
|
||||
codePrintExperimentalWarning()
|
||||
logger.Debugf("Starting code analysis")
|
||||
|
||||
if len(codeAppDirectories) == 0 {
|
||||
return fmt.Errorf("no source code directory provided")
|
||||
}
|
||||
|
||||
if len(codeImportDirectories) == 0 {
|
||||
return fmt.Errorf("no import directory provided")
|
||||
}
|
||||
|
||||
if utils.IsEmptyString(codeGraphDatabase) {
|
||||
return fmt.Errorf("no database path provided")
|
||||
}
|
||||
|
||||
codeRepoCfg := code.FileSystemSourceRepositoryConfig{
|
||||
SourcePaths: codeAppDirectories,
|
||||
ImportPaths: codeImportDirectories,
|
||||
}
|
||||
|
||||
codeRepo, err := code.NewFileSystemSourceRepository(codeRepoCfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create source repository: %w", err)
|
||||
}
|
||||
|
||||
codeLang, err := codeGetLanguage()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create source language: %w", err)
|
||||
}
|
||||
|
||||
codeRepo.ConfigureForLanguage(codeLang)
|
||||
|
||||
graph, err := graph.NewPropertyGraph(&graph.LocalPropertyGraphConfig{
|
||||
Name: "code-analysis",
|
||||
DatabasePath: codeGraphDatabase,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create graph database: %w", err)
|
||||
}
|
||||
|
||||
builderConfig := code.CodeGraphBuilderConfig{
|
||||
RecursiveImport: true,
|
||||
}
|
||||
|
||||
builder, err := code.NewCodeGraphBuilder(builderConfig, codeRepo, codeLang, graph)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create code graph builder: %w", err)
|
||||
}
|
||||
|
||||
redirectLogToFile(logFile)
|
||||
|
||||
var fileProcessedTracker any
|
||||
var importsProcessedTracker any
|
||||
var functionsProcessedTracker any
|
||||
|
||||
builder.RegisterEventHandler("ui-callback",
|
||||
func(event code.CodeGraphBuilderEvent, metrics code.CodeGraphBuilderMetrics) error {
|
||||
switch event.Kind {
|
||||
case code.CodeGraphBuilderEventFileQueued:
|
||||
ui.IncrementTrackerTotal(fileProcessedTracker, 1)
|
||||
case code.CodeGraphBuilderEventFileProcessed:
|
||||
ui.IncrementProgress(fileProcessedTracker, 1)
|
||||
}
|
||||
|
||||
ui.UpdateValue(importsProcessedTracker, int64(metrics.ImportsCount))
|
||||
ui.UpdateValue(functionsProcessedTracker, int64(metrics.FunctionsCount))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
ui.StartProgressWriter()
|
||||
|
||||
fileProcessedTracker = ui.TrackProgress("Processing source files", 0)
|
||||
importsProcessedTracker = ui.TrackProgress("Processing imports", 0)
|
||||
functionsProcessedTracker = ui.TrackProgress("Processing functions", 0)
|
||||
|
||||
err = builder.Build()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build code graph: %w", err)
|
||||
}
|
||||
|
||||
ui.MarkTrackerAsDone(fileProcessedTracker)
|
||||
ui.MarkTrackerAsDone(importsProcessedTracker)
|
||||
ui.MarkTrackerAsDone(functionsProcessedTracker)
|
||||
ui.StopProgressWriter()
|
||||
|
||||
logger.Debugf("Code analysis completed")
|
||||
return nil
|
||||
}
|
||||
|
||||
func codePrintExperimentalWarning() {
|
||||
ui.PrintWarning("Code analysis is experimental and may have breaking change")
|
||||
}
|
||||
|
||||
func codeGetLanguage() (code.SourceLanguage, error) {
|
||||
lang := strings.ToLower(codeLanguage)
|
||||
switch lang {
|
||||
case "python":
|
||||
return languages.NewPythonSourceLanguage()
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported language: %s", codeLanguage)
|
||||
}
|
||||
}
|
||||
87
docs/docs/guides/code-analysis.md
Normal file
87
docs/docs/guides/code-analysis.md
Normal file
@ -0,0 +1,87 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
title: 🏄 Code Analysis
|
||||
---
|
||||
|
||||
# Code Analysis
|
||||
|
||||
:::note
|
||||
|
||||
EXPERIMENTAL: This feature is experimental and may introduce breaking changes.
|
||||
|
||||
:::
|
||||
|
||||
`vet` has a code analysis framework built on top of [tree-sitter](#) parsers. The goal
|
||||
of this framework is to support multiple languages, source repositories (local and remote),
|
||||
and create a representation of code that can be analysed for common software
|
||||
supply chain security related use-cases such as
|
||||
|
||||
- Identify shadowed imports
|
||||
- Identify evidence of a dependency actually being used
|
||||
- Import reachability analysis
|
||||
- Function reachability analysis
|
||||
|
||||
:::warning
|
||||
|
||||
The code analysis framework is designed specifically to be simple, fast and
|
||||
not to be a full-fledged static analysis tool. It is currently in early stages
|
||||
of development and may not support all languages or maintain API compatibility.
|
||||
|
||||
:::
|
||||
|
||||
## Build a Code Analysis Database
|
||||
|
||||
- Analyse code and build a database for further analysis.
|
||||
|
||||
```bash
|
||||
vet code --db /tmp/code.db \
|
||||
--src /path/to/app \
|
||||
--imports /virtualenvs/app/lib/python3.11/site-packages \
|
||||
--lang python \
|
||||
create-db
|
||||
```
|
||||
|
||||
The above command does the following:
|
||||
|
||||
- Uses Python as the language for parsing source code
|
||||
- Analyses application code recursively in `/path/to/app`
|
||||
- Analyses dependencies in `/virtualenvs/app/lib/python3.11/site-packages`
|
||||
- Creates a database at `/tmp/code.db` for further analysis
|
||||
|
||||
## Manual Query Execution
|
||||
|
||||
Use [cayleygraph](https://cayley.gitbook.io/cayley/) to query the database.
|
||||
|
||||
```bash
|
||||
docker run -it -p 64210:64210 -v /tmp/code.db:/db cayleygraph/cayley -a /db -d bolt
|
||||
```
|
||||
|
||||
- Navigate to `http://127.0.0.1:64210` in your browser
|
||||
|
||||
### Query Examples
|
||||
|
||||
#### Dependency Graph
|
||||
|
||||
Build dependency graph for your application
|
||||
|
||||
```js
|
||||
g.V().Tag("source").out("imports").Tag("target").all()
|
||||
```
|
||||
|
||||

|
||||
|
||||
#### Import Reachability
|
||||
|
||||
Check if a specific import is reachable in your application
|
||||
|
||||
```js
|
||||
g.V("app").followRecursive(g.M().out("imports")).is("six").all()
|
||||
```
|
||||
|
||||
- `app` is the application originating from `app.py`
|
||||
- `six` is a python module imported transitively
|
||||
|
||||
### Query API
|
||||
|
||||
Refer to [Gizmo Query Language](https://cayley.gitbook.io/cayley/query-languages/gizmoapi)
|
||||
for documentation on constructing custom queries.
|
||||
BIN
docs/static/img/vet-code-demo-import-graph.png
vendored
Normal file
BIN
docs/static/img/vet-code-demo-import-graph.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 300 KiB |
28
go.mod
28
go.mod
@ -7,6 +7,8 @@ toolchain go1.22.1
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.0
|
||||
github.com/cayleygraph/cayley v0.7.7
|
||||
github.com/cayleygraph/quad v1.2.5
|
||||
github.com/cli/oauth v1.0.1
|
||||
github.com/deepmap/oapi-codegen v1.16.3
|
||||
github.com/gofri/go-github-ratelimit v1.1.0
|
||||
@ -25,7 +27,7 @@ require (
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/oauth2 v0.21.0
|
||||
google.golang.org/protobuf v1.34.1
|
||||
google.golang.org/protobuf v1.34.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
@ -44,13 +46,19 @@ require (
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/boltdb/bolt v1.3.1 // indirect
|
||||
github.com/bytedance/sonic v1.11.8 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/chainguard-dev/git-urls v1.0.2 // indirect
|
||||
github.com/cloudflare/circl v1.3.8 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dennwc/base v1.0.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
||||
github.com/dop251/goja v0.0.0-20190105122144-6d5bf35058fa // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/flosch/pongo2/v4 v4.0.2 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
|
||||
@ -59,14 +67,22 @@ require (
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.21.0 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.2+incompatible // indirect
|
||||
github.com/gobuffalo/envy v1.7.1 // indirect
|
||||
github.com/gobuffalo/logger v1.0.1 // indirect
|
||||
github.com/gobuffalo/packd v0.3.0 // indirect
|
||||
github.com/gobuffalo/packr/v2 v2.7.1 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/hidal-go/hidalgo v0.0.0-20190814174001-42e03f3b5eaa // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/iris-contrib/schema v0.0.6 // indirect
|
||||
github.com/joho/godotenv v1.3.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kataras/blocks v0.0.8 // indirect
|
||||
@ -93,8 +109,15 @@ require (
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/oklog/ulid/v2 v2.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/piprate/json-gold v0.3.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/prometheus/client_golang v1.19.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.0 // indirect
|
||||
github.com/prometheus/common v0.50.0 // indirect
|
||||
github.com/prometheus/procfs v0.13.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
@ -102,6 +125,7 @@ require (
|
||||
github.com/tdewolff/minify/v2 v2.20.33 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.7.14 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/tylertreat/BoomFilters v0.0.0-20181028192813-611b3dbe80e8 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
@ -115,10 +139,12 @@ require (
|
||||
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect
|
||||
golang.org/x/mod v0.18.0 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/term v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.22.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
||||
366
go.sum
366
go.sum
@ -1,5 +1,11 @@
|
||||
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.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
|
||||
@ -14,15 +20,22 @@ github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
|
||||
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0=
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM=
|
||||
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/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
|
||||
github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9 h1:6COpXWpHbhWM1wgcQN95TdsmrLTba8KQfPgImBXzkjA=
|
||||
github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
|
||||
@ -30,12 +43,21 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/badgerodon/peg v0.0.0-20130729175151-9e5f7f4d07ca/go.mod h1:TWe0N2hv5qvpLHT+K16gYcGBllld4h65dQ/5CNuirmk=
|
||||
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/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
@ -43,12 +65,22 @@ github.com/bytedance/sonic v1.11.8 h1:Zw/j1KfiS+OYTi9lyB3bb0CFxPJVkM17k1wyDG32LR
|
||||
github.com/bytedance/sonic v1.11.8/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cayleygraph/cayley v0.7.7 h1:z+7xkAbg6bKiXJOtOkEG3zCm2K084sr/aGwFV7xcQNs=
|
||||
github.com/cayleygraph/cayley v0.7.7/go.mod h1:VUd+PInYf94/VY41ePeFtFyP99BAs953kFT4N+6F7Ko=
|
||||
github.com/cayleygraph/quad v1.1.0/go.mod h1:maWODEekEhrO0mdc9h5n/oP7cH1h/OTgqQ2qWbuI9M4=
|
||||
github.com/cayleygraph/quad v1.2.5 h1:su0zl5KJGrLGGyMwbuzO6bK6miJPJnXHETS1DdXKjLY=
|
||||
github.com/cayleygraph/quad v1.2.5/go.mod h1:cyhGxjj93c2fcs3/vjVQmWmChzcJTz3cu1/GkesNLnY=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
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/chainguard-dev/git-urls v1.0.2 h1:pSpT7ifrpc5X55n4aTTm7FFUE+ZQHKiqpiwNkJrVcKQ=
|
||||
github.com/chainguard-dev/git-urls v1.0.2/go.mod h1:rbGgj10OS7UgZlbzdUQIQpT0k/D4+An04HJY7Ol+Y/o=
|
||||
github.com/cli/browser v1.0.0/go.mod h1:IEWkHYbLjkhtjwwWlwTHW2lGxeS5gezEQBMLTwDHf5Q=
|
||||
github.com/cli/oauth v1.0.1 h1:pXnTFl/qUegXHK531Dv0LNjW4mLx626eS42gnzfXJPA=
|
||||
github.com/cli/oauth v1.0.1/go.mod h1:qd/FX8ZBD6n1sVNQO3aIdRxeu5LGw9WhKnYhIIoC2A4=
|
||||
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI=
|
||||
github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
||||
@ -58,14 +90,51 @@ github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ=
|
||||
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
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-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
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.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cznic/mathutil v0.0.0-20170313102836-1447ad269d64 h1:oad14P7M0/ZAPSMH1nl1vC8zdKVkA3kfHLO59z1l8Eg=
|
||||
github.com/cznic/mathutil v0.0.0-20170313102836-1447ad269d64/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
|
||||
github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U=
|
||||
github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
|
||||
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/deepmap/oapi-codegen v1.16.3 h1:GT9G86SbQtT1r8ZB+4Cybi9VGdu1P5ieNvNdEoCSbrA=
|
||||
github.com/deepmap/oapi-codegen v1.16.3/go.mod h1:JD6ErqeX0nYnhdciLc61Konj3NBASREMlkHOgHn8WAM=
|
||||
github.com/dennwc/base v1.0.0 h1:xlBzvBNRvkQ1LFI/jom7rr0vZsvYDKtvMM6lIpjFb3M=
|
||||
github.com/dennwc/base v1.0.0/go.mod h1:zaTDIiAcg2oKW9XhjIaRc1kJVteCFXSSW6jwmCedUaI=
|
||||
github.com/dennwc/graphql v0.0.0-20180603144102-12cfed44bc5d/go.mod h1:lg9KQn0BgRCSCGNpcGvJp/0Ljf1Yxk8TZq9HSYc43fk=
|
||||
github.com/dgraph-io/badger v1.5.4/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ=
|
||||
github.com/dgraph-io/badger v1.5.5/go.mod h1:QgCntgIUPsjnp7cMLhUybJHb7iIoQWAHT6tF8ngCjWk=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-farm v0.0.0-20190416075124-e1214b5e05dc/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dlclark/regexp2 v1.1.4/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/docker/docker v0.7.3-0.20180412203414-a422774e593b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dop251/goja v0.0.0-20190105122144-6d5bf35058fa h1:cA2OMt2CQ2yq2WhQw16mHv6ej9YY07H4pzfR/z/y+1Q=
|
||||
github.com/dop251/goja v0.0.0-20190105122144-6d5bf35058fa/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
|
||||
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/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI=
|
||||
github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
|
||||
@ -74,14 +143,30 @@ github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/flimzy/diff v0.1.5/go.mod h1:lFJtC7SPsK0EroDmGTSrdtWKAxOk3rO+q+e04LL05Hs=
|
||||
github.com/flimzy/diff v0.1.6/go.mod h1:lFJtC7SPsK0EroDmGTSrdtWKAxOk3rO+q+e04LL05Hs=
|
||||
github.com/flimzy/kivik v1.8.1/go.mod h1:S2aPycbG0eDFll4wgXt9uacSNkXISPufutnc9sv+mdA=
|
||||
github.com/flimzy/testy v0.1.16/go.mod h1:3szguN8NXqgq9bt9Gu8TQVj698PJWmyx/VY1frwwKrM=
|
||||
github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=
|
||||
github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=
|
||||
github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsouza/go-dockerclient v1.2.2/go.mod h1:KpcjM623fQYE9MZiTGzKhjfxXAV9wbyX2C1cyRHfhl0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
|
||||
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kivik/couchdb v1.8.1/go.mod h1:5XJRkAMpBlEVA4q0ktIZjUPYBjoBmRoiWvwUBzP3BOQ=
|
||||
github.com/go-kivik/kivik v1.8.1/go.mod h1:nIuJ8z4ikBrVUSk3Ua8NoDqYKULPNjuddjqRvlSUyyQ=
|
||||
github.com/go-kivik/kiviktest v1.1.2/go.mod h1:JdhVyzixoYhoIDUt6hRf1yAfYyaDa5/u9SDOindDkfQ=
|
||||
github.com/go-kivik/pouchdb v1.3.5/go.mod h1:U+siUrqLCVxeMU3QjQTYIC3/F/e6EUKm+o5buJb7vpw=
|
||||
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-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
@ -90,22 +175,52 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.21.0 h1:4fZA11ovvtkdgaeev9RGWPgc1uj3H8W+rNYyH/ySBb0=
|
||||
github.com/go-playground/validator/v10 v10.21.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-sourcemap/sourcemap v2.1.2+incompatible h1:0b/xya7BKGhXuqFESKM4oIiRo9WOt2ebz7KxfreD6ug=
|
||||
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
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/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||
github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8=
|
||||
github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
|
||||
github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg=
|
||||
github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
||||
github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
|
||||
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
|
||||
github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o=
|
||||
github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/gofri/go-github-ratelimit v1.1.0 h1:ijQ2bcv5pjZXNil5FiwglCg8wc9s8EgjTmNkqjw8nuk=
|
||||
github.com/gofri/go-github-ratelimit v1.1.0/go.mod h1:OnCi5gV+hAG/LMR7llGhU7yHt44se9sYgKPnafoL7RY=
|
||||
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.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
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-20190129154638-5b532d6fd5ef/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/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.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
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.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2 h1:yEt5djSYb4iNtmV9iJGVday+i4e9u6Mrn5iP64HH5QM=
|
||||
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
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/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84=
|
||||
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg=
|
||||
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.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
@ -116,31 +231,61 @@ github.com/google/go-github/v54 v54.0.0/go.mod h1:Sw1LXWHhXRZtzJ9LI5fyJg9wbQzYvF
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/osv-scanner v1.7.4 h1:u021ANn9P/xZUPpYXVv7O1n9FYhF+YmYEqt/05BLpPA=
|
||||
github.com/google/osv-scanner v1.7.4/go.mod h1:GWXnZYfJEeICRUFJIbW/BRHzsDHVkqPOvKo5uBi5+jU=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
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.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/jsbuiltin v0.0.0-20180426082241-50091555e127/go.mod h1:7X1acUyFRf+oVFTU6SWw9mnb57Vxn+Nbh8iPbKg95hs=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
|
||||
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/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/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/hidal-go/hidalgo v0.0.0-20190814174001-42e03f3b5eaa h1:hBE4LGxApbZiV/3YoEPv7uYlUMWOogG1hwtkpiU87zQ=
|
||||
github.com/hidal-go/hidalgo v0.0.0-20190814174001-42e03f3b5eaa/go.mod h1:bPkrxDlroXxigw8BMWTEPTv4W5/rQwNgg2BECXsgyX0=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go=
|
||||
github.com/iris-contrib/httpexpect/v2 v2.15.2/go.mod h1:JLDgIqnFy5loDSUv1OA2j0mb6p/rDhiCqigP22Uq9xE=
|
||||
github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=
|
||||
github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=
|
||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
|
||||
github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.9 h1:ACteMBRrrmm1gMsXe9PSTOClQ63IXDUt03H5U+UV8OU=
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.9/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM=
|
||||
github.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg=
|
||||
github.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=
|
||||
@ -155,13 +300,22 @@ github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=
|
||||
github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
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.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
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/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.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/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
@ -174,8 +328,16 @@ github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
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/linkeddata/gojsonld v0.0.0-20170418210642-4f5db6791326/go.mod h1:nfqkuSNlsk1bvti/oa7TThx4KmRMBmSxf3okHI9wp3E=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
|
||||
github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
|
||||
github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
@ -187,13 +349,17 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
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/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@ -201,23 +367,77 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
|
||||
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
|
||||
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/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/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/selinux v1.0.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
|
||||
github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U=
|
||||
github.com/package-url/packageurl-go v0.1.3 h1:4juMED3hHiz0set3Vq3KeQ75KD1avthoXLtmE3I0PLs=
|
||||
github.com/package-url/packageurl-go v0.1.3/go.mod h1:nKAWB8E6uk1MHqiS/lQb9pYBGH2+mdJ2PJc2s50dQY0=
|
||||
github.com/pandatix/go-cvss v0.6.2 h1:TFiHlzUkT67s6UkelHmK6s1INKVUG7nlKYiWWDTITGI=
|
||||
github.com/pandatix/go-cvss v0.6.2/go.mod h1:jDXYlQBZrc8nvrMUVVvTG8PhmuShOnKrxP53nOFkt8Q=
|
||||
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
|
||||
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.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/piprate/json-gold v0.3.0 h1:a1vHx7Q1jOO1pjCtKwTI/WCzwaQwRt9VM7apK2uy200=
|
||||
github.com/piprate/json-gold v0.3.0/go.mod h1:OK1z7UgtBZk06n2cDE2OSq1kffmjFFp5/2yhLLCz9UM=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
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.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||
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.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
|
||||
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
|
||||
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.50.0 h1:YSZE6aa9+luNa2da6/Tik0q0A5AbR+U003TItK57CPQ=
|
||||
github.com/prometheus/common v0.50.0/go.mod h1:wHFBCEVWVmHMUpg7pYcOm2QUR/ocQdYSJVQJKnHc3xQ=
|
||||
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.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
|
||||
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/safedep/dry v0.0.0-20240405050202-3b26d9386e57 h1:gOd2ZOZByHdLww3IhhtTAtsit4S9u2BZAAhk7yAR2gk=
|
||||
@ -226,31 +446,50 @@ github.com/safedep/go-sarif/v2 v2.3.1 h1:/LTv5BrWQ7tUvfFYXaqB0l5lhwlrKW5NDGqY6Qe
|
||||
github.com/safedep/go-sarif/v2 v2.3.1/go.mod h1:MSqMMx9WqlBSY7pXoOZWgEsVB4FDNfhcaXDA1j6Sr+w=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
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.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smacker/go-tree-sitter v0.0.0-20240514083259-c5d1f3f5f99e h1:nOtrYzQNgRipVsrOkYpVU/ZMDS3/+78It68cPBPKy8A=
|
||||
github.com/smacker/go-tree-sitter v0.0.0-20240514083259-c5d1f3f5f99e/go.mod h1:q99oHDsbP0xRwmn7Vmob8gbSMNyvJ83OauXPSuHQuKE=
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
|
||||
github.com/spdx/tools-golang v0.5.4 h1:fRW4iz16P1ZCUtWStFqS6YiMgnK7WgfTFU/lrsYlvqY=
|
||||
github.com/spdx/tools-golang v0.5.4/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE=
|
||||
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/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
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 v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
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/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
||||
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
||||
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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@ -260,6 +499,7 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tdewolff/minify/v2 v2.20.33 h1:lZFesDQagd+zGxyC3fEO/X2jZWB8CrahKi77lNrgAAQ=
|
||||
github.com/tdewolff/minify/v2 v2.20.33/go.mod h1:1TJni7+mATKu24cBQQpgwakrYRD27uC1/rdJOgdv8ns=
|
||||
github.com/tdewolff/parse/v2 v2.7.14 h1:100KJ+QAO3PpMb3uUjzEU/NpmCdbBYz6KPmCIAfWpR8=
|
||||
@ -269,8 +509,14 @@ github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo=
|
||||
github.com/terminalstatic/go-xsd-validate v0.1.5/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/tylertreat/BoomFilters v0.0.0-20181028192813-611b3dbe80e8 h1:7X4KYG3guI2mPQGxm/ZNNsiu4BjKnef0KG0TblMC+Z8=
|
||||
github.com/tylertreat/BoomFilters v0.0.0-20181028192813-611b3dbe80e8/go.mod h1:OYRfF6eb5wY9VRFkXJH8FFBi3plw2v+giaIu7P054pM=
|
||||
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/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
@ -283,12 +529,16 @@ github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21
|
||||
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
|
||||
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
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/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
||||
github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=
|
||||
@ -297,37 +547,74 @@ github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCO
|
||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
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/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
|
||||
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.mongodb.org/mongo-driver v1.0.4/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM=
|
||||
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
|
||||
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/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.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.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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-20181114220301-adae6a3d119a/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-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/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-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-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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
@ -336,16 +623,41 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
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-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
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-20201020160332-67f06af15bc9/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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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-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-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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190614160838-b47fdc937951/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191009170203-06d7bd2c5f4f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -368,6 +680,7 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||
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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@ -378,37 +691,82 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
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.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
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-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-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-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/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-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
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.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
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=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
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/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-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 h1:QW9+G6Fir4VcRXVH8x3LilNAb6cxBGLa6+GM4hRwexE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3/go.mod h1:kdrSS/OiLkPrNUpzD4aHgCq2rVuC/YRxok32HXZ4vRE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
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.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/olivere/elastic.v5 v5.0.80/go.mod h1:uhHoB4o3bvX5sorxBU29rPcmBQdV2Qfg0FBrx5D6pV0=
|
||||
gopkg.in/olivere/elastic.v5 v5.0.81/go.mod h1:uhHoB4o3bvX5sorxBU29rPcmBQdV2Qfg0FBrx5D6pV0=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
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.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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
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=
|
||||
k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak=
|
||||
k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=
|
||||
|
||||
@ -14,7 +14,7 @@ func StartProgressWriter() {
|
||||
|
||||
pw.SetAutoStop(false)
|
||||
pw.SetTrackerLength(25)
|
||||
pw.SetMessageWidth(20)
|
||||
pw.SetMessageLength(20)
|
||||
pw.SetSortBy(progress.SortByPercentDsc)
|
||||
pw.SetStyle(progress.StyleDefault)
|
||||
pw.SetOutputWriter(os.Stderr)
|
||||
@ -72,6 +72,12 @@ func IncrementProgress(i any, count int64) {
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateValue(i any, count int64) {
|
||||
if tracker, ok := i.(*progress.Tracker); ok {
|
||||
tracker.SetValue(count)
|
||||
}
|
||||
}
|
||||
|
||||
func progressTrackerDelta(tracker *progress.Tracker) int64 {
|
||||
return (tracker.Total - tracker.Value())
|
||||
}
|
||||
|
||||
@ -21,6 +21,11 @@ func PrintMsg(s string, args ...any) {
|
||||
fmt.Fprint(os.Stderr, text.Bold.Sprint(msg), "\n")
|
||||
}
|
||||
|
||||
func PrintWarning(s string, args ...any) {
|
||||
msg := fmt.Sprintf(s, args...)
|
||||
fmt.Fprint(os.Stdout, text.FgYellow.Sprint(msg), "\n")
|
||||
}
|
||||
|
||||
func PrintError(s string, args ...any) {
|
||||
msg := fmt.Sprintf(s, args...)
|
||||
fmt.Fprint(os.Stderr, text.FgRed.Sprint(msg), "\n")
|
||||
|
||||
1
main.go
1
main.go
@ -61,6 +61,7 @@ func main() {
|
||||
cmd.AddCommand(newAuthCommand())
|
||||
cmd.AddCommand(newScanCommand())
|
||||
cmd.AddCommand(newQueryCommand())
|
||||
cmd.AddCommand(newCodeCommand())
|
||||
cmd.AddCommand(newVersionCommand())
|
||||
cmd.AddCommand(newConnectCommand())
|
||||
|
||||
|
||||
31
pkg/code/code.go
Normal file
31
pkg/code/code.go
Normal file
@ -0,0 +1,31 @@
|
||||
package code
|
||||
|
||||
// Things that we can do
|
||||
// 1. Prune unused direct dependencies based on code usage
|
||||
// 2. Find places in 1P code where a vulnerable library is imported
|
||||
// 3. Find places in 1P code where a call to a vulnerable function is made
|
||||
// 4. Find path from 1P code to a vulnerable function in direct or transitive dependencies
|
||||
// 5. Find path from 1P code to a vulnerable library in direct or transitive dependencies
|
||||
|
||||
// Primitives that we need
|
||||
// 1. Source code parsing
|
||||
// 2. Import resolution to local 1P code or imported files in 3P code
|
||||
// 3. Graph datastructure to represent a function call graph across 1P and 3P code
|
||||
// 4. Graph datastructure to represent a file import graph across 1P and 3P code
|
||||
//
|
||||
// Source code parsing should provide
|
||||
// 1. Enumerate imported 3P code
|
||||
// 2. Enumerate functions in the source code
|
||||
// 3. Enumerate function calls to 1P or 3P code
|
||||
//
|
||||
// Code Property Graph (CPG), stitching 1P and 3P code
|
||||
// into a queryable graph datastructure for analyzers having
|
||||
//
|
||||
// Future enhancements should include ability to enrich function nodes
|
||||
// with meta information such as contributors, last modified time, use-case tags etc.
|
||||
|
||||
// CONCEPTS used in building the framework
|
||||
// 1. Source: Used to represent a mechanism to find and enumerate source files
|
||||
// 2. Language: Used to represent the domain of programming languages
|
||||
// 3. Node: Used to represent an in-memory representation of a node in an AST / CST
|
||||
// 4. Entity: Used to represent a node that can be persisted in a property graph (analysis and query domain)
|
||||
363
pkg/code/code_graph.go
Normal file
363
pkg/code/code_graph.go
Normal file
@ -0,0 +1,363 @@
|
||||
package code
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/safedep/vet/pkg/code/entities"
|
||||
"github.com/safedep/vet/pkg/code/nodes"
|
||||
"github.com/safedep/vet/pkg/common/logger"
|
||||
"github.com/safedep/vet/pkg/storage/graph"
|
||||
)
|
||||
|
||||
type CodeGraphBuilderConfig struct {
|
||||
// Resolve imports to file and load them
|
||||
RecursiveImport bool
|
||||
|
||||
// Code analyser concurrency
|
||||
Concurrency int
|
||||
}
|
||||
|
||||
type CodeGraphBuilderMetrics struct {
|
||||
GoRoutineCount int
|
||||
ErrorCount int
|
||||
FilesProcessed int
|
||||
FilesInQueue int
|
||||
ImportsCount int
|
||||
FunctionsCount int
|
||||
}
|
||||
|
||||
type CodeGraphBuilderEvent struct {
|
||||
Kind string
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
const (
|
||||
CodeGraphBuilderEventFileQueued = "file_queued"
|
||||
CodeGraphBuilderEventFileProcessed = "file_processed"
|
||||
CodeGraphBuilderEventImportProcessed = "import_processed"
|
||||
CodeGraphBuilderEventFunctionProcessed = "function_processed"
|
||||
)
|
||||
|
||||
// The event handler is invoked from its own go routine. Handler functions
|
||||
// must be thread safe.
|
||||
type CodeGraphBuilderEventHandler func(CodeGraphBuilderEvent, CodeGraphBuilderMetrics) error
|
||||
|
||||
type codeGraphBuilder struct {
|
||||
config CodeGraphBuilderConfig
|
||||
eventHandlers map[string]CodeGraphBuilderEventHandler
|
||||
metrics CodeGraphBuilderMetrics
|
||||
|
||||
repository SourceRepository
|
||||
lang SourceLanguage
|
||||
storage graph.Graph
|
||||
|
||||
// Queue for processing files
|
||||
fileQueue chan SourceFile
|
||||
fileQueueWg *sync.WaitGroup
|
||||
fileQueueLock *sync.Mutex
|
||||
|
||||
fileCache map[string]bool
|
||||
functionDeclCache map[string]string
|
||||
functionCallCache map[string]string
|
||||
}
|
||||
|
||||
func NewCodeGraphBuilder(config CodeGraphBuilderConfig,
|
||||
repository SourceRepository,
|
||||
lang SourceLanguage,
|
||||
storage graph.Graph) (*codeGraphBuilder, error) {
|
||||
// Concurrency will cause issues with the the common cache
|
||||
if config.Concurrency <= 0 {
|
||||
config.Concurrency = 1
|
||||
}
|
||||
|
||||
return &codeGraphBuilder{config: config,
|
||||
repository: repository,
|
||||
lang: lang,
|
||||
storage: storage,
|
||||
eventHandlers: make(map[string]CodeGraphBuilderEventHandler, 0),
|
||||
metrics: CodeGraphBuilderMetrics{GoRoutineCount: config.Concurrency},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *codeGraphBuilder) RegisterEventHandler(name string, handler CodeGraphBuilderEventHandler) {
|
||||
if _, ok := b.eventHandlers[name]; ok {
|
||||
logger.Warnf("Event handler already registered: %s", name)
|
||||
return
|
||||
}
|
||||
|
||||
b.eventHandlers[name] = handler
|
||||
}
|
||||
|
||||
func (b *codeGraphBuilder) Build() error {
|
||||
// Reinitialize the file queue if needed
|
||||
if b.fileQueue != nil {
|
||||
close(b.fileQueue)
|
||||
}
|
||||
|
||||
b.fileQueue = make(chan SourceFile, 10000)
|
||||
b.fileQueueWg = &sync.WaitGroup{}
|
||||
b.fileQueueLock = &sync.Mutex{}
|
||||
|
||||
b.fileCache = make(map[string]bool)
|
||||
b.functionDeclCache = make(map[string]string)
|
||||
b.functionCallCache = make(map[string]string)
|
||||
|
||||
logger.Debugf("Building code graph using repository: %s", b.repository.Name())
|
||||
|
||||
for i := 0; i < b.config.Concurrency; i++ {
|
||||
go b.fileProcessor(b.fileQueueWg)
|
||||
}
|
||||
|
||||
err := b.repository.EnumerateSourceFiles(func(file SourceFile) error {
|
||||
b.enqueueSourceFile(file)
|
||||
return nil
|
||||
})
|
||||
|
||||
b.fileQueueWg.Wait()
|
||||
|
||||
close(b.fileQueue)
|
||||
b.fileQueue = nil
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *codeGraphBuilder) enqueueSourceFile(file SourceFile) {
|
||||
b.synchronized(func() {
|
||||
b.metrics.FilesInQueue++
|
||||
|
||||
if _, ok := b.fileCache[file.Path]; ok {
|
||||
logger.Debugf("Skipping already processed file: %s", file.Path)
|
||||
return
|
||||
}
|
||||
|
||||
b.fileQueueWg.Add(1)
|
||||
b.fileQueue <- file
|
||||
|
||||
// TODO: Optimize this. Storing entire file path is not needed
|
||||
b.fileCache[file.Path] = true
|
||||
})
|
||||
|
||||
b.notifyEventHandlers(CodeGraphBuilderEvent{
|
||||
Kind: CodeGraphBuilderEventFileQueued,
|
||||
Data: file,
|
||||
}, b.metrics)
|
||||
}
|
||||
|
||||
func (b *codeGraphBuilder) fileProcessor(wg *sync.WaitGroup) {
|
||||
for file := range b.fileQueue {
|
||||
err := b.buildForFile(file)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to process code graph for: %s: %v", file.Path, err)
|
||||
|
||||
b.synchronized(func() {
|
||||
b.metrics.ErrorCount++
|
||||
})
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
|
||||
b.synchronized(func() {
|
||||
b.metrics.FilesProcessed++
|
||||
})
|
||||
|
||||
b.notifyEventHandlers(CodeGraphBuilderEvent{
|
||||
Kind: CodeGraphBuilderEventFileProcessed,
|
||||
Data: file,
|
||||
}, b.metrics)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *codeGraphBuilder) buildForFile(file SourceFile) error {
|
||||
logger.Debugf("Parsing source file: %s", file.Path)
|
||||
|
||||
cst, err := b.lang.ParseSource(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer cst.Close()
|
||||
|
||||
b.processSourceFileNode(file)
|
||||
b.processImportNodes(cst, file)
|
||||
b.processFunctionDeclarations(cst, file)
|
||||
b.processFunctionCalls(cst, file)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *codeGraphBuilder) processSourceFileNode(file SourceFile) {
|
||||
// What?
|
||||
}
|
||||
|
||||
func (b *codeGraphBuilder) processImportNodes(cst *nodes.CST, currentFile SourceFile) {
|
||||
// Get the module name for the source file we are processing
|
||||
// This serves as the current node in the graph
|
||||
currentModuleName, err := LangMapFileToModule(currentFile, b.repository, b.lang, b.useImports())
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to map file to module: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
thisNode := b.buildPackageNode(currentModuleName,
|
||||
currentModuleName, currentFile.Path, b.importSourceName(currentFile))
|
||||
|
||||
importNodes, err := b.lang.GetImportNodes(cst)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to get import nodes: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, importNode := range importNodes {
|
||||
logger.Debugf("Processing import node: %s", importNode.ImportName())
|
||||
|
||||
// Try to resolve the imported package to file for further processing
|
||||
sourceFile, err := LangMapModuleToFile(importNode.ImportName(), currentFile,
|
||||
b.repository, b.lang, b.useImports())
|
||||
if err != nil {
|
||||
logger.Warnf("Failed to map import node: '%s' to file: %v", importNode.ImportName(), err)
|
||||
} else {
|
||||
logger.Debugf("Import node: %s resolved to path: %s",
|
||||
importNode.ImportName(), sourceFile.Path)
|
||||
|
||||
if b.useImports() {
|
||||
b.enqueueSourceFile(sourceFile)
|
||||
}
|
||||
}
|
||||
|
||||
// Import name may be current file relative in which case we fix it up
|
||||
// to appropriate module name to avoid duplicate nodes in the graph
|
||||
importNodeName := importNode.ImportName()
|
||||
if len(sourceFile.Path) > 0 {
|
||||
fixedImportName, err := LangMapFileToModule(sourceFile, b.repository, b.lang, b.useImports())
|
||||
if err != nil {
|
||||
logger.Errorf("[Import Fixing]: Failed to map file to module: %v", err)
|
||||
return
|
||||
} else {
|
||||
importNodeName = fixedImportName
|
||||
}
|
||||
}
|
||||
|
||||
// Finally add the imported package node to the graph
|
||||
importedPkgNode := b.buildPackageNode(importNodeName, importNodeName,
|
||||
sourceFile.Path, b.importSourceName(sourceFile))
|
||||
|
||||
err = b.storage.Link(thisNode.Imports(&importedPkgNode))
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to link import node: %v", err)
|
||||
}
|
||||
|
||||
b.synchronized(func() {
|
||||
b.metrics.ImportsCount++
|
||||
})
|
||||
|
||||
b.notifyEventHandlers(CodeGraphBuilderEvent{
|
||||
Kind: CodeGraphBuilderEventImportProcessed,
|
||||
Data: importNode.ImportName(),
|
||||
}, b.metrics)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *codeGraphBuilder) processFunctionDeclarations(cst *nodes.CST, currentFile SourceFile) {
|
||||
moduleName, err := LangMapFileToModule(currentFile, b.repository, b.lang, b.useImports())
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to map file to module: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
functionDecls, err := b.lang.GetFunctionDeclarationNodes(cst)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to get function declaration nodes: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
thisNode := b.buildPackageNode(moduleName, moduleName,
|
||||
currentFile.Path, b.importSourceName(currentFile))
|
||||
|
||||
for _, functionDecl := range functionDecls {
|
||||
if fid, ok := b.functionDeclCache[moduleName]; ok {
|
||||
if fid == functionDecl.Id() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
logger.Debugf("Processing function declaration: %s/%s",
|
||||
moduleName,
|
||||
functionDecl.Id())
|
||||
|
||||
fnNode := entities.FunctionDecl{
|
||||
Id: functionDecl.Id(),
|
||||
SourceFilePath: currentFile.Path,
|
||||
SourceFileType: b.importSourceName(currentFile),
|
||||
FunctionName: functionDecl.Name(),
|
||||
ContainerName: functionDecl.Container(),
|
||||
}
|
||||
|
||||
err = b.storage.Link(thisNode.DeclaresFunction(&fnNode))
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to link function declaration: %v", err)
|
||||
}
|
||||
|
||||
b.synchronized(func() {
|
||||
b.metrics.FunctionsCount++
|
||||
})
|
||||
|
||||
b.notifyEventHandlers(CodeGraphBuilderEvent{
|
||||
Kind: CodeGraphBuilderEventFunctionProcessed,
|
||||
Data: functionDecl.Id(),
|
||||
}, b.metrics)
|
||||
|
||||
b.functionDeclCache[moduleName] = functionDecl.Id()
|
||||
}
|
||||
}
|
||||
|
||||
// The function call graph will be built as follows:
|
||||
// [Function] -> [FunctionCall] -> [Function]
|
||||
//
|
||||
// # To build this, we will
|
||||
//
|
||||
// - Enumerate all function declarations
|
||||
// - Assign an unique ID for the function (source)
|
||||
// - Enumerate all function calls within the function body
|
||||
// - Resolve the function call to a function declaration
|
||||
// - Assign an unique ID to the called function (target)
|
||||
// - Store the relationship in the graph
|
||||
func (b *codeGraphBuilder) processFunctionCalls(cst *nodes.CST, currentFile SourceFile) {
|
||||
// Building a function call graph through static analysis is harder than expected
|
||||
}
|
||||
|
||||
func (b *codeGraphBuilder) useImports() bool {
|
||||
return b.config.RecursiveImport
|
||||
}
|
||||
|
||||
func (b *codeGraphBuilder) buildPackageNode(id, name, path, src string) entities.Package {
|
||||
return entities.Package{
|
||||
Id: id,
|
||||
Name: name,
|
||||
SourceFilePath: path,
|
||||
SourceFileType: src,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *codeGraphBuilder) importSourceName(file SourceFile) string {
|
||||
if file.IsImportedFile() {
|
||||
return entities.PackageEntitySourceTypeImport
|
||||
} else {
|
||||
return entities.PackageEntitySourceTypeApp
|
||||
}
|
||||
}
|
||||
|
||||
func (b *codeGraphBuilder) notifyEventHandlers(event CodeGraphBuilderEvent, metrics CodeGraphBuilderMetrics) {
|
||||
for name, handler := range b.eventHandlers {
|
||||
err := handler(event, metrics)
|
||||
if err != nil {
|
||||
logger.Warnf("Failed to notify event handler: %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *codeGraphBuilder) synchronized(fn func()) {
|
||||
b.fileQueueLock.Lock()
|
||||
defer b.fileQueueLock.Unlock()
|
||||
|
||||
fn()
|
||||
}
|
||||
28
pkg/code/entities/function_decl.go
Normal file
28
pkg/code/entities/function_decl.go
Normal file
@ -0,0 +1,28 @@
|
||||
package entities
|
||||
|
||||
const (
|
||||
FunctionDeclEntity = "function_decl"
|
||||
|
||||
FunctionDeclPropertyName = "name"
|
||||
FunctionDeclPropertyContainerName = "containerName"
|
||||
FunctionDeclPropertySourceFilePath = "sourceFilePath"
|
||||
FunctionDeclPropertySourceFileType = "sourceFileType"
|
||||
)
|
||||
|
||||
type FunctionDecl struct {
|
||||
Id string
|
||||
|
||||
ContainerName string
|
||||
FunctionName string
|
||||
SourceFilePath string
|
||||
SourceFileType string
|
||||
}
|
||||
|
||||
func (f *FunctionDecl) Properties() map[string]string {
|
||||
return map[string]string{
|
||||
FunctionDeclPropertyName: f.FunctionName,
|
||||
FunctionDeclPropertyContainerName: f.ContainerName,
|
||||
FunctionDeclPropertySourceFilePath: f.SourceFilePath,
|
||||
FunctionDeclPropertySourceFileType: f.SourceFileType,
|
||||
}
|
||||
}
|
||||
68
pkg/code/entities/package.go
Normal file
68
pkg/code/entities/package.go
Normal file
@ -0,0 +1,68 @@
|
||||
package entities
|
||||
|
||||
import "github.com/safedep/vet/pkg/storage/graph"
|
||||
|
||||
const (
|
||||
// The entity type
|
||||
PackageEntity = "package"
|
||||
|
||||
// Entity specific constants
|
||||
PackageEntitySourceTypeApp = "app"
|
||||
PackageEntitySourceTypeImport = "import"
|
||||
|
||||
// Property names
|
||||
PackagePropertyName = "name"
|
||||
PackagePropertySourceFilePath = "sourceFilePath"
|
||||
PackagePropertySourceFileType = "sourceFileType"
|
||||
|
||||
// Relationship names
|
||||
PackageRelationshipImports = "imports"
|
||||
PackageRelationshipDeclaresFunction = "declares_function"
|
||||
)
|
||||
|
||||
type Package struct {
|
||||
Id string
|
||||
Name string
|
||||
SourceFilePath string
|
||||
SourceFileType string
|
||||
}
|
||||
|
||||
func (p *Package) Properties() map[string]string {
|
||||
return map[string]string{
|
||||
PackagePropertyName: p.Name,
|
||||
PackagePropertySourceFilePath: p.SourceFilePath,
|
||||
PackagePropertySourceFileType: p.SourceFileType,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Package) Imports(anotherPackage *Package) *graph.Edge {
|
||||
return &graph.Edge{
|
||||
Name: PackageRelationshipImports,
|
||||
From: &graph.Node{
|
||||
ID: p.Id,
|
||||
Label: PackageEntity,
|
||||
Properties: p.Properties(),
|
||||
},
|
||||
To: &graph.Node{
|
||||
ID: anotherPackage.Id,
|
||||
Label: PackageEntity,
|
||||
Properties: anotherPackage.Properties(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Package) DeclaresFunction(fn *FunctionDecl) *graph.Edge {
|
||||
return &graph.Edge{
|
||||
Name: PackageRelationshipDeclaresFunction,
|
||||
From: &graph.Node{
|
||||
ID: p.Id,
|
||||
Label: PackageEntity,
|
||||
Properties: p.Properties(),
|
||||
},
|
||||
To: &graph.Node{
|
||||
ID: fn.Id,
|
||||
Label: FunctionDeclEntity,
|
||||
Properties: fn.Properties(),
|
||||
},
|
||||
}
|
||||
}
|
||||
38
pkg/code/lang.go
Normal file
38
pkg/code/lang.go
Normal file
@ -0,0 +1,38 @@
|
||||
package code
|
||||
|
||||
import (
|
||||
"github.com/safedep/vet/pkg/code/nodes"
|
||||
)
|
||||
|
||||
// Declarative metadata for the source language
|
||||
type SourceLanguageMeta struct {
|
||||
SourceFileGlobs []string
|
||||
}
|
||||
|
||||
// Any source language implementation must support these
|
||||
// primitives for integration with the code analysis system
|
||||
type SourceLanguage interface {
|
||||
// Get metadata for the source language
|
||||
GetMeta() SourceLanguageMeta
|
||||
|
||||
// Parse a source file and return the CST (tree-sitter concrete syntax tree)
|
||||
ParseSource(file SourceFile) (*nodes.CST, error)
|
||||
|
||||
// Get import nodes from the CST
|
||||
GetImportNodes(cst *nodes.CST) ([]nodes.CSTImportNode, error)
|
||||
|
||||
// Get function declaration nodes from the CST
|
||||
GetFunctionDeclarationNodes(cst *nodes.CST) ([]nodes.CSTFunctionNode, error)
|
||||
|
||||
// Get function call nodes from the CST
|
||||
GetFunctionCallNodes(cst *nodes.CST) ([]nodes.CSTFunctionCallNode, error)
|
||||
|
||||
// Resolve the import module / package name from relative path
|
||||
ResolveImportNameFromPath(relPath string) (string, error)
|
||||
|
||||
// Resolve import name to possible relative file names
|
||||
// Multiple paths are possible because an import such
|
||||
// as a.b can resolve to a/b.py or a/b/__init__.py depending
|
||||
// on language and file system
|
||||
ResolveImportPathsFromName(currentFile SourceFile, importName string, includeImports bool) ([]string, error)
|
||||
}
|
||||
62
pkg/code/languages/common.go
Normal file
62
pkg/code/languages/common.go
Normal file
@ -0,0 +1,62 @@
|
||||
package languages
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/safedep/vet/pkg/code"
|
||||
"github.com/safedep/vet/pkg/code/nodes"
|
||||
sitter "github.com/smacker/go-tree-sitter"
|
||||
)
|
||||
|
||||
// Base implementation of a common source language
|
||||
type commonSourceLanguage struct {
|
||||
parser *sitter.Parser
|
||||
lang *sitter.Language
|
||||
}
|
||||
|
||||
func (l *commonSourceLanguage) ParseSource(file code.SourceFile) (*nodes.CST, error) {
|
||||
fileReader, err := file.Open()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open source file: %w", err)
|
||||
}
|
||||
|
||||
defer fileReader.Close()
|
||||
|
||||
data, err := io.ReadAll(fileReader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read source: %w", err)
|
||||
}
|
||||
|
||||
tree, err := l.parser.ParseCtx(context.Background(), nil, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nodes.NewCST(tree, l.lang, data), nil
|
||||
}
|
||||
|
||||
func (l *commonSourceLanguage) GetImportNodes(cst *nodes.CST) ([]nodes.CSTImportNode, error) {
|
||||
return nil, fmt.Errorf("language does not support import nodes")
|
||||
}
|
||||
|
||||
func (l *commonSourceLanguage) GetFunctionDeclarationNodes(cst *nodes.CST) ([]nodes.CSTFunctionNode, error) {
|
||||
return nil, fmt.Errorf("language does not support function declaration nodes")
|
||||
}
|
||||
|
||||
func (l *commonSourceLanguage) GetFunctionCallNodes(cst *nodes.CST) ([]nodes.CSTFunctionCallNode, error) {
|
||||
return nil, fmt.Errorf("language does not support function call nodes")
|
||||
}
|
||||
|
||||
func (l *commonSourceLanguage) ResolveImportNameFromPath(relPath string) (string, error) {
|
||||
return "", fmt.Errorf("language does not support import name resolution")
|
||||
}
|
||||
|
||||
func (l *commonSourceLanguage) ResolveImportPathsFromName(importName string) ([]string, error) {
|
||||
return nil, fmt.Errorf("language does not support import path resolution")
|
||||
}
|
||||
|
||||
func (l *commonSourceLanguage) GetMeta() code.SourceLanguageMeta {
|
||||
return code.SourceLanguageMeta{}
|
||||
}
|
||||
233
pkg/code/languages/python.go
Normal file
233
pkg/code/languages/python.go
Normal file
@ -0,0 +1,233 @@
|
||||
package languages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/safedep/vet/pkg/code"
|
||||
"github.com/safedep/vet/pkg/code/nodes"
|
||||
"github.com/safedep/vet/pkg/common/logger"
|
||||
sitter "github.com/smacker/go-tree-sitter"
|
||||
"github.com/smacker/go-tree-sitter/python"
|
||||
)
|
||||
|
||||
type pythonSourceLanguage struct {
|
||||
commonSourceLanguage
|
||||
}
|
||||
|
||||
func NewPythonSourceLanguage() (code.SourceLanguage, error) {
|
||||
parser := sitter.NewParser()
|
||||
parser.SetLanguage(python.GetLanguage())
|
||||
|
||||
return &pythonSourceLanguage{
|
||||
commonSourceLanguage: commonSourceLanguage{
|
||||
parser: parser,
|
||||
lang: python.GetLanguage(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *pythonSourceLanguage) GetMeta() code.SourceLanguageMeta {
|
||||
return code.SourceLanguageMeta{
|
||||
SourceFileGlobs: []string{"*.py"},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *pythonSourceLanguage) GetImportNodes(cst *nodes.CST) ([]nodes.CSTImportNode, error) {
|
||||
// Tree Sitter query to get import nodes in Python
|
||||
// The order of capture names are important because the extraction
|
||||
// uses capture index
|
||||
query := `
|
||||
(import_statement
|
||||
name: ((dotted_name) @module_name))
|
||||
|
||||
(import_from_statement
|
||||
module_name: (dotted_name) @module_name
|
||||
name: (dotted_name
|
||||
(identifier) @submodule_name @submodule_alias))
|
||||
|
||||
(import_from_statement
|
||||
module_name: (relative_import) @module_name
|
||||
name: (dotted_name
|
||||
(identifier) @submodule_name @submodule_alias))
|
||||
|
||||
(import_statement
|
||||
name: (aliased_import
|
||||
name: ((dotted_name) @module_name @submodule_name)
|
||||
alias: (identifier) @submodule_alias))
|
||||
|
||||
(import_from_statement
|
||||
module_name: (dotted_name) @module_name
|
||||
name: (aliased_import
|
||||
name: (dotted_name
|
||||
(identifier) @submodule_name)
|
||||
alias: (identifier) @submodule_alias))
|
||||
|
||||
(import_from_statement
|
||||
module_name: (relative_import) @module_name
|
||||
name: (aliased_import
|
||||
name: ((dotted_name) @submodule_name)
|
||||
alias: (identifier) @submodule_alias))
|
||||
`
|
||||
|
||||
importNodes := []nodes.CSTImportNode{}
|
||||
err := code.TSExecQuery(query, python.GetLanguage(),
|
||||
cst.Code(),
|
||||
cst.Root(),
|
||||
func(m *sitter.QueryMatch, _ *sitter.Query, ok bool) error {
|
||||
importNode := nodes.NewCSTImportNode(cst).
|
||||
WithModuleName(m.Captures[0].Node)
|
||||
|
||||
if len(m.Captures) > 1 {
|
||||
importNode = importNode.WithModuleItem(m.Captures[1].Node)
|
||||
}
|
||||
|
||||
if len(m.Captures) > 2 {
|
||||
importNode = importNode.WithModuleAlias(m.Captures[2].Node)
|
||||
}
|
||||
|
||||
logger.Debugf("Found imported module: %s (%s) as (%s)",
|
||||
importNode.ImportName(),
|
||||
importNode.ImportItem(),
|
||||
importNode.ImportAlias())
|
||||
|
||||
importNodes = append(importNodes, *importNode)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return importNodes, err
|
||||
}
|
||||
|
||||
func (l *pythonSourceLanguage) GetFunctionDeclarationNodes(cst *nodes.CST) ([]nodes.CSTFunctionNode, error) {
|
||||
query := `
|
||||
(function_definition
|
||||
name: (identifier) @function_name
|
||||
parameters: (parameters) @function_args
|
||||
body: (block) @function_body) @function_declaration
|
||||
`
|
||||
functionNodes := []nodes.CSTFunctionNode{}
|
||||
err := code.TSExecQuery(query, python.GetLanguage(),
|
||||
cst.Code(),
|
||||
cst.Root(),
|
||||
func(m *sitter.QueryMatch, _ *sitter.Query, ok bool) error {
|
||||
if len(m.Captures) != 4 {
|
||||
return fmt.Errorf("expected 4 captures, got %d", len(m.Captures))
|
||||
}
|
||||
|
||||
functionNode := nodes.NewCSTFunctionNode(cst).
|
||||
WithDeclaration(m.Captures[0].Node).
|
||||
WithName(m.Captures[1].Node).
|
||||
WithArgs(m.Captures[2].Node).
|
||||
WithBody(m.Captures[3].Node)
|
||||
|
||||
// Find the containing class if any by walking up the tree
|
||||
parent := functionNode.Declaration().Parent()
|
||||
for parent != nil {
|
||||
if parent.Type() == "class_definition" {
|
||||
functionNode = functionNode.WithContainer(parent.ChildByFieldName("name"))
|
||||
break
|
||||
}
|
||||
|
||||
parent = parent.Parent()
|
||||
}
|
||||
|
||||
logger.Debugf("Found function declaration: %s/%s",
|
||||
functionNode.Container(),
|
||||
functionNode.Name())
|
||||
|
||||
functionNodes = append(functionNodes, *functionNode)
|
||||
return nil
|
||||
})
|
||||
|
||||
return functionNodes, err
|
||||
}
|
||||
|
||||
func (l *pythonSourceLanguage) GetFunctionCallNodes(cst *nodes.CST) ([]nodes.CSTFunctionCallNode, error) {
|
||||
query := `
|
||||
(call
|
||||
function: (identifier) @function_name
|
||||
arguments: (argument_list) @arguments) @function_call
|
||||
|
||||
(call
|
||||
function: (attribute
|
||||
object: (identifier) @object
|
||||
attribute: (identifier) @function_name)
|
||||
arguments: (argument_list) @arguments) @function_call
|
||||
`
|
||||
callNodes := []nodes.CSTFunctionCallNode{}
|
||||
err := code.TSExecQuery(query, python.GetLanguage(),
|
||||
cst.Code(),
|
||||
cst.Root(),
|
||||
func(m *sitter.QueryMatch, q *sitter.Query, ok bool) error {
|
||||
if len(m.Captures) < 3 {
|
||||
return fmt.Errorf("expected at least 3 captures, got %d", len(m.Captures))
|
||||
}
|
||||
|
||||
functionCallNode := nodes.NewCSTFunctionCallNode(cst).WithCall(m.Captures[0].Node)
|
||||
|
||||
n := len(m.Captures)
|
||||
switch n {
|
||||
case 3:
|
||||
functionCallNode = functionCallNode.WithCallee(m.Captures[1].Node)
|
||||
functionCallNode = functionCallNode.WithArgs(m.Captures[2].Node)
|
||||
case 4:
|
||||
functionCallNode = functionCallNode.WithReceiver(m.Captures[1].Node)
|
||||
functionCallNode = functionCallNode.WithCallee(m.Captures[2].Node)
|
||||
functionCallNode = functionCallNode.WithArgs(m.Captures[3].Node)
|
||||
}
|
||||
|
||||
logger.Debugf("Found function call: (%s).%s",
|
||||
functionCallNode.Receiver(),
|
||||
functionCallNode.Callee())
|
||||
|
||||
callNodes = append(callNodes, *functionCallNode)
|
||||
return nil
|
||||
})
|
||||
|
||||
return callNodes, err
|
||||
}
|
||||
|
||||
func (l *pythonSourceLanguage) ResolveImportNameFromPath(relPath string) (string, error) {
|
||||
if relPath[0] == '/' {
|
||||
return "", fmt.Errorf("path is not relative: %s", relPath)
|
||||
}
|
||||
|
||||
relPath = strings.TrimSuffix(relPath, "__init__.py")
|
||||
relPath = strings.TrimSuffix(relPath, "/")
|
||||
relPath = strings.TrimSuffix(relPath, ".py")
|
||||
|
||||
relPath = strings.ReplaceAll(relPath, "/", ".")
|
||||
|
||||
return relPath, nil
|
||||
}
|
||||
|
||||
func (l *pythonSourceLanguage) ResolveImportPathsFromName(currentFile code.SourceFile,
|
||||
importName string, includeImports bool) ([]string, error) {
|
||||
paths := []string{}
|
||||
|
||||
if len(importName) == 0 {
|
||||
return paths, fmt.Errorf("import name is empty")
|
||||
}
|
||||
|
||||
// If its a relative import, resolve it to the root
|
||||
if importName[0] == '.' {
|
||||
currDir := filepath.Dir(currentFile.Path)
|
||||
relativeImportName := filepath.Join(currDir, importName[1:])
|
||||
|
||||
rootRelativePath, err := currentFile.Repository().GetRelativePath(relativeImportName, includeImports)
|
||||
if err != nil {
|
||||
return paths, fmt.Errorf("failed to get relative path: %w", err)
|
||||
}
|
||||
|
||||
importName = rootRelativePath
|
||||
}
|
||||
|
||||
importName = strings.ReplaceAll(importName, ".", "/")
|
||||
|
||||
paths = append(paths, importName+".py")
|
||||
paths = append(paths, importName+"/__init__.py")
|
||||
|
||||
return paths, nil
|
||||
}
|
||||
56
pkg/code/languages/python_test.go
Normal file
56
pkg/code/languages/python_test.go
Normal file
@ -0,0 +1,56 @@
|
||||
package languages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestResolveImportNameFromPath(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
path string
|
||||
expected string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Path is module",
|
||||
path: "module.py",
|
||||
expected: "module",
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "Path is module with subdirectory",
|
||||
path: "subdir/module.py",
|
||||
expected: "subdir.module",
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "Path is module with subdirectory and init",
|
||||
path: "subdir/__init__.py",
|
||||
expected: "subdir",
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "Absolute path",
|
||||
path: "/subdir/module.py",
|
||||
expected: "",
|
||||
err: fmt.Errorf("path is not relative: /subdir/module.py"),
|
||||
},
|
||||
}
|
||||
|
||||
l := &pythonSourceLanguage{}
|
||||
for _, test := range cases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
res, err := l.ResolveImportNameFromPath(test.path)
|
||||
|
||||
if test.err != nil {
|
||||
assert.ErrorContains(t, err, test.err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, test.expected, res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
47
pkg/code/nodes/cst.go
Normal file
47
pkg/code/nodes/cst.go
Normal file
@ -0,0 +1,47 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
sitter "github.com/smacker/go-tree-sitter"
|
||||
)
|
||||
|
||||
// Represents a Concreate Syntax Tree (CST) of a *single* source file
|
||||
// We will use TreeSitter as the only supported parser.
|
||||
// However, we can have language specific wrappers over TreeSitter CST
|
||||
// to make it easy for high level modules to operate
|
||||
type CST struct {
|
||||
tree *sitter.Tree
|
||||
lang *sitter.Language
|
||||
code []byte
|
||||
}
|
||||
|
||||
func NewCST(tree *sitter.Tree, lang *sitter.Language, code []byte) *CST {
|
||||
return &CST{tree: tree, lang: lang, code: code}
|
||||
}
|
||||
|
||||
func (n *CST) Close() {
|
||||
n.tree.Close()
|
||||
}
|
||||
|
||||
func (n *CST) Root() *sitter.Node {
|
||||
return n.tree.RootNode()
|
||||
}
|
||||
|
||||
func (n *CST) Code() []byte {
|
||||
return n.code
|
||||
}
|
||||
|
||||
func (n *CST) SubTree(node *sitter.Node) (*CST, error) {
|
||||
p := sitter.NewParser()
|
||||
p.SetLanguage(n.lang)
|
||||
|
||||
data := []byte(node.Content(n.code))
|
||||
|
||||
t, err := p.ParseCtx(context.Background(), nil, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewCST(t, n.lang, data), nil
|
||||
}
|
||||
67
pkg/code/nodes/function_call.go
Normal file
67
pkg/code/nodes/function_call.go
Normal file
@ -0,0 +1,67 @@
|
||||
package nodes
|
||||
|
||||
import sitter "github.com/smacker/go-tree-sitter"
|
||||
|
||||
type CSTFunctionCallNode struct {
|
||||
cst *CST
|
||||
caller *CSTFunctionNode
|
||||
|
||||
call *sitter.Node
|
||||
receiver *sitter.Node
|
||||
callee *sitter.Node
|
||||
args *sitter.Node
|
||||
}
|
||||
|
||||
func NewCSTFunctionCallNode(cst *CST) *CSTFunctionCallNode {
|
||||
return &CSTFunctionCallNode{cst: cst}
|
||||
}
|
||||
|
||||
// Should we mutate the receiver or make a copy?
|
||||
func (n *CSTFunctionCallNode) WithCaller(caller *CSTFunctionNode) *CSTFunctionCallNode {
|
||||
n.caller = caller
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *CSTFunctionCallNode) WithCall(call *sitter.Node) *CSTFunctionCallNode {
|
||||
n.call = call
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *CSTFunctionCallNode) WithReceiver(receiver *sitter.Node) *CSTFunctionCallNode {
|
||||
n.receiver = receiver
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *CSTFunctionCallNode) WithCallee(callee *sitter.Node) *CSTFunctionCallNode {
|
||||
n.callee = callee
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *CSTFunctionCallNode) WithArgs(args *sitter.Node) *CSTFunctionCallNode {
|
||||
n.args = args
|
||||
return n
|
||||
}
|
||||
|
||||
func (n CSTFunctionCallNode) CallNode() *sitter.Node {
|
||||
return n.call
|
||||
}
|
||||
|
||||
func (n CSTFunctionCallNode) ReceiverNode() *sitter.Node {
|
||||
return n.receiver
|
||||
}
|
||||
|
||||
func (n CSTFunctionCallNode) Receiver() string {
|
||||
if n.receiver != nil {
|
||||
return n.receiver.Content(n.cst.code)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (n CSTFunctionCallNode) Callee() string {
|
||||
if n.callee != nil {
|
||||
return n.callee.Content(n.cst.code)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
88
pkg/code/nodes/function_decl.go
Normal file
88
pkg/code/nodes/function_decl.go
Normal file
@ -0,0 +1,88 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sitter "github.com/smacker/go-tree-sitter"
|
||||
)
|
||||
|
||||
type CSTFunctionNode struct {
|
||||
cst *CST
|
||||
|
||||
// The function declaration
|
||||
declaration *sitter.Node
|
||||
|
||||
// Container node, such as a class or a module
|
||||
container *sitter.Node
|
||||
|
||||
// Name of the function
|
||||
name *sitter.Node
|
||||
|
||||
// Arguments of the function
|
||||
args *sitter.Node
|
||||
|
||||
// Body of the function
|
||||
body *sitter.Node
|
||||
}
|
||||
|
||||
func NewCSTFunctionNode(cst *CST) *CSTFunctionNode {
|
||||
return &CSTFunctionNode{cst: cst}
|
||||
}
|
||||
|
||||
func (n *CSTFunctionNode) WithDeclaration(declaration *sitter.Node) *CSTFunctionNode {
|
||||
n.declaration = declaration
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *CSTFunctionNode) WithContainer(container *sitter.Node) *CSTFunctionNode {
|
||||
n.container = container
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *CSTFunctionNode) WithName(name *sitter.Node) *CSTFunctionNode {
|
||||
n.name = name
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *CSTFunctionNode) WithArgs(args *sitter.Node) *CSTFunctionNode {
|
||||
n.args = args
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *CSTFunctionNode) WithBody(body *sitter.Node) *CSTFunctionNode {
|
||||
n.body = body
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *CSTFunctionNode) Declaration() *sitter.Node {
|
||||
return n.declaration
|
||||
}
|
||||
|
||||
func (n *CSTFunctionNode) ContainerNode() *sitter.Node {
|
||||
return n.container
|
||||
}
|
||||
|
||||
func (n *CSTFunctionNode) NameNode() *sitter.Node {
|
||||
return n.name
|
||||
}
|
||||
|
||||
// Human readable Id for use in graph query
|
||||
func (n CSTFunctionNode) Id() string {
|
||||
return fmt.Sprintf("%s/%s", n.Container(), n.Name())
|
||||
}
|
||||
|
||||
func (n CSTFunctionNode) Name() string {
|
||||
if n.name != nil {
|
||||
return n.name.Content(n.cst.code)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (n CSTFunctionNode) Container() string {
|
||||
if n.container != nil {
|
||||
return n.container.Content(n.cst.code)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
53
pkg/code/nodes/import.go
Normal file
53
pkg/code/nodes/import.go
Normal file
@ -0,0 +1,53 @@
|
||||
package nodes
|
||||
|
||||
import sitter "github.com/smacker/go-tree-sitter"
|
||||
|
||||
type CSTImportNode struct {
|
||||
cst *CST
|
||||
moduleNameNode *sitter.Node
|
||||
moduleItemNode *sitter.Node
|
||||
moduleAliasNode *sitter.Node
|
||||
}
|
||||
|
||||
func NewCSTImportNode(cst *CST) *CSTImportNode {
|
||||
return &CSTImportNode{cst: cst}
|
||||
}
|
||||
|
||||
func (n *CSTImportNode) WithModuleName(moduleName *sitter.Node) *CSTImportNode {
|
||||
n.moduleNameNode = moduleName
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *CSTImportNode) WithModuleItem(moduleItem *sitter.Node) *CSTImportNode {
|
||||
n.moduleItemNode = moduleItem
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *CSTImportNode) WithModuleAlias(moduleAlias *sitter.Node) *CSTImportNode {
|
||||
n.moduleAliasNode = moduleAlias
|
||||
return n
|
||||
}
|
||||
|
||||
func (n CSTImportNode) ImportName() string {
|
||||
if n.moduleNameNode != nil {
|
||||
return n.moduleNameNode.Content(n.cst.code)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (n CSTImportNode) ImportItem() string {
|
||||
if n.moduleItemNode != nil {
|
||||
return n.moduleItemNode.Content(n.cst.code)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (n CSTImportNode) ImportAlias() string {
|
||||
if n.moduleAliasNode != nil {
|
||||
return n.moduleAliasNode.Content(n.cst.code)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
6
pkg/code/nodes/nodes.go
Normal file
6
pkg/code/nodes/nodes.go
Normal file
@ -0,0 +1,6 @@
|
||||
package nodes
|
||||
|
||||
// The nodes package represent the concept of in-memory
|
||||
// CST / AST nodes, ideally as language agnostic as possible.
|
||||
// These are essentially wrappers over Tree Sitter nodes with
|
||||
// some helpful methods to extract information from them.
|
||||
68
pkg/code/source.go
Normal file
68
pkg/code/source.go
Normal file
@ -0,0 +1,68 @@
|
||||
package code
|
||||
|
||||
import "io"
|
||||
|
||||
// Represents a source file. This is required because import resolution
|
||||
// algorithm may need to know the path of the current file
|
||||
type SourceFile struct {
|
||||
// Identifier for the source file. This is dependent on the
|
||||
// language on how to uniquely identify a source file
|
||||
Id string
|
||||
|
||||
// Repository specific path to the file
|
||||
Path string
|
||||
|
||||
// The repository from where the source file is retrieved
|
||||
repository SourceRepository
|
||||
}
|
||||
|
||||
func (f SourceFile) Open() (io.ReadCloser, error) {
|
||||
return f.repository.OpenSourceFile(f)
|
||||
}
|
||||
|
||||
func (f SourceFile) Repository() SourceRepository {
|
||||
return f.repository
|
||||
}
|
||||
|
||||
// Check if the source file is imported. Returns true
|
||||
// if the source file is relative to the source directories
|
||||
// without considering any import directories
|
||||
func (f SourceFile) IsImportedFile() bool {
|
||||
// When we don't have a repository, we assume that the file
|
||||
// is not valid. Let us consider it to be an unresolved import
|
||||
if f.repository == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
_, err := f.repository.GetRelativePath(f.Path, false)
|
||||
return (err != nil)
|
||||
}
|
||||
|
||||
type sourceFileHandlerFn func(file SourceFile) error
|
||||
|
||||
// SourceRepository is a repository of source files. It can be a local
|
||||
// repository such as a directory or a remote repository such as a git repository.
|
||||
// The concept of repository is just to find 1P and 3P code while abstracting the
|
||||
// underlying storage mechanism (e.g. local file system or remote git repository)
|
||||
type SourceRepository interface {
|
||||
// Name of the repository
|
||||
Name() string
|
||||
|
||||
// Enumerate all source files in the repository. This can be multiple
|
||||
// directories for a local source repository
|
||||
EnumerateSourceFiles(handler sourceFileHandlerFn) error
|
||||
|
||||
// Get a source file by path. This function enumerates all directories
|
||||
// available in the repository to check for existence of the source file by path
|
||||
GetSourceFileByPath(path string, includeImports bool) (SourceFile, error)
|
||||
|
||||
// Open a source file for reading
|
||||
OpenSourceFile(file SourceFile) (io.ReadCloser, error)
|
||||
|
||||
// Configure the repository for a source language
|
||||
ConfigureForLanguage(language SourceLanguage)
|
||||
|
||||
// Get relative path of the source file from the repository root
|
||||
// This is useful for constructing the import path. The first match is returned
|
||||
GetRelativePath(path string, includeImport bool) (string, error)
|
||||
}
|
||||
180
pkg/code/source_fs.go
Normal file
180
pkg/code/source_fs.go
Normal file
@ -0,0 +1,180 @@
|
||||
package code
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/safedep/vet/pkg/common/logger"
|
||||
)
|
||||
|
||||
type FileSystemSourceRepositoryConfig struct {
|
||||
// List of paths to search for source code
|
||||
SourcePaths []string
|
||||
|
||||
// Glob patterns to exclude from source code search
|
||||
ExcludedGlobs []string
|
||||
|
||||
// Glob patterns to include in source code search
|
||||
IncludedGlobs []string
|
||||
|
||||
// List of paths to search for imported source code
|
||||
ImportPaths []string
|
||||
}
|
||||
|
||||
type fileSystemSourceRepository struct {
|
||||
config FileSystemSourceRepositoryConfig
|
||||
}
|
||||
|
||||
func NewFileSystemSourceRepository(config FileSystemSourceRepositoryConfig) (SourceRepository, error) {
|
||||
return &fileSystemSourceRepository{config: config}, nil
|
||||
}
|
||||
|
||||
func (r *fileSystemSourceRepository) Name() string {
|
||||
return "FileSystemSourceRepository"
|
||||
}
|
||||
|
||||
func (r *fileSystemSourceRepository) GetRelativePath(path string, includeImportPaths bool) (string, error) {
|
||||
lookupPaths := []string{}
|
||||
|
||||
lookupPaths = append(lookupPaths, r.config.SourcePaths...)
|
||||
if includeImportPaths {
|
||||
lookupPaths = append(lookupPaths, r.config.ImportPaths...)
|
||||
}
|
||||
|
||||
for _, rootPath := range lookupPaths {
|
||||
if relPath, err := filepath.Rel(rootPath, path); err == nil {
|
||||
if strings.HasPrefix(relPath, "..") {
|
||||
continue
|
||||
}
|
||||
|
||||
return relPath, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("path not found in source or import paths: %s", path)
|
||||
}
|
||||
|
||||
func (r *fileSystemSourceRepository) ConfigureForLanguage(language SourceLanguage) {
|
||||
langMeta := language.GetMeta()
|
||||
|
||||
r.config.IncludedGlobs = append(r.config.IncludedGlobs, langMeta.SourceFileGlobs...)
|
||||
}
|
||||
|
||||
func (r *fileSystemSourceRepository) EnumerateSourceFiles(handler sourceFileHandlerFn) error {
|
||||
logger.Debugf("Enumerating source files with config: %v", r.config)
|
||||
|
||||
for _, sourcePath := range r.config.SourcePaths {
|
||||
if err := r.enumSourceDir(sourcePath, handler); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSourceFileByPath searches for a source file by path in the repository.
|
||||
// It starts by searching the source paths and then the import paths.
|
||||
//
|
||||
// TODO: This is a problem. Source file lookup is ecosystem specific. For example
|
||||
// Python and Ruby may have different rules to lookup a source file by path during
|
||||
// import operation. May be we need to make the operations explicit at repository level.
|
||||
func (r *fileSystemSourceRepository) GetSourceFileByPath(path string, includeImports bool) (SourceFile, error) {
|
||||
lookupPaths := []string{}
|
||||
|
||||
// Import paths are generally are higher precedence than source paths
|
||||
if includeImports {
|
||||
lookupPaths = append(lookupPaths, r.config.ImportPaths...)
|
||||
}
|
||||
|
||||
lookupPaths = append(lookupPaths, r.config.SourcePaths...)
|
||||
|
||||
for _, sourcePath := range lookupPaths {
|
||||
st, err := os.Stat(sourcePath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !st.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
sourceFilePath := filepath.Join(sourcePath, path)
|
||||
if _, err := os.Stat(sourceFilePath); err == nil {
|
||||
return SourceFile{
|
||||
Path: sourceFilePath,
|
||||
repository: r,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return SourceFile{}, fmt.Errorf("source file not found: %s", path)
|
||||
}
|
||||
|
||||
func (r *fileSystemSourceRepository) OpenSourceFile(file SourceFile) (io.ReadCloser, error) {
|
||||
return os.OpenFile(file.Path, os.O_RDONLY, 0)
|
||||
}
|
||||
|
||||
func (r *fileSystemSourceRepository) enumSourceDir(path string, handler sourceFileHandlerFn) error {
|
||||
return filepath.WalkDir(path, func(path string, info os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !r.isAcceptableSourceFile(path) {
|
||||
logger.Debugf("Ignoring file: %s", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
return handler(SourceFile{
|
||||
Path: path,
|
||||
repository: r,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// A source file is acceptable if the base file name meets the following conditions:
|
||||
//
|
||||
// 1. It does not match any of the excluded globs
|
||||
// 2. It matches any of the included globs
|
||||
//
|
||||
// If there are no included globs, then all files are acceptable.
|
||||
func (r *fileSystemSourceRepository) isAcceptableSourceFile(path string) bool {
|
||||
baseFileName := filepath.Base(path)
|
||||
|
||||
for _, excludedGlob := range r.config.ExcludedGlobs {
|
||||
matched, err := filepath.Match(excludedGlob, baseFileName)
|
||||
if err != nil {
|
||||
logger.Errorf("Invalid exclude glob pattern: %s: %v", excludedGlob, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if matched {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.config.IncludedGlobs) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, includedGlob := range r.config.IncludedGlobs {
|
||||
matched, err := filepath.Match(includedGlob, baseFileName)
|
||||
if err != nil {
|
||||
logger.Errorf("Invalid include glob pattern: %s: %v", includedGlob, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if matched {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
146
pkg/code/source_fs_test.go
Normal file
146
pkg/code/source_fs_test.go
Normal file
@ -0,0 +1,146 @@
|
||||
package code
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsAcceptableSourceFile(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
path string
|
||||
excludedGlobs []string
|
||||
includedGlobs []string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
"No globs",
|
||||
"/a/b/foo.go",
|
||||
[]string{},
|
||||
[]string{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"Excluded glob",
|
||||
"/a/b/foo.go",
|
||||
[]string{"*.go"},
|
||||
[]string{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Included glob",
|
||||
"/a/b/foo.go",
|
||||
[]string{},
|
||||
[]string{"*.go"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"Excluded and included globs",
|
||||
"/a/b/foo.go",
|
||||
[]string{"*.go"},
|
||||
[]string{"*.go"},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range cases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
repo := fileSystemSourceRepository{
|
||||
config: FileSystemSourceRepositoryConfig{
|
||||
ExcludedGlobs: test.excludedGlobs,
|
||||
IncludedGlobs: test.includedGlobs,
|
||||
},
|
||||
}
|
||||
|
||||
ret := repo.isAcceptableSourceFile(test.path)
|
||||
assert.Equal(t, test.want, ret)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRelativePath(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
path string
|
||||
sourcePaths []string
|
||||
importPaths []string
|
||||
includeImports bool
|
||||
want string
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
"Relative in source path with no imports",
|
||||
"/a/b/c/foo.go",
|
||||
[]string{"/a/b"},
|
||||
[]string{},
|
||||
false,
|
||||
"c/foo.go",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Relative in source path with imports",
|
||||
"/a/b/c/foo.go",
|
||||
[]string{"/a/b"},
|
||||
[]string{"/a/b/c"},
|
||||
true,
|
||||
"c/foo.go",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Relative in import path with imports",
|
||||
"/a/b/c/foo.go",
|
||||
[]string{"/x/y"},
|
||||
[]string{"/a/b"},
|
||||
true,
|
||||
"c/foo.go",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"First match in import path",
|
||||
"/a/b/c/foo.go",
|
||||
[]string{"/x/y"},
|
||||
[]string{"/a/b", "/a/b/c"},
|
||||
true,
|
||||
"c/foo.go",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Relative in source path",
|
||||
"./a/b/c/foo.go",
|
||||
[]string{"./a/b"},
|
||||
[]string{"/a/b/c"},
|
||||
true,
|
||||
"c/foo.go",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"No match",
|
||||
"/a/b/c/foo.go",
|
||||
[]string{"/x/y"},
|
||||
[]string{"/z"},
|
||||
true,
|
||||
"",
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range cases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
repo := fileSystemSourceRepository{
|
||||
config: FileSystemSourceRepositoryConfig{
|
||||
SourcePaths: test.sourcePaths,
|
||||
ImportPaths: test.importPaths,
|
||||
},
|
||||
}
|
||||
|
||||
ret, err := repo.GetRelativePath(test.path, test.includeImports)
|
||||
if test.err {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, test.want, ret)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
80
pkg/code/utils.go
Normal file
80
pkg/code/utils.go
Normal file
@ -0,0 +1,80 @@
|
||||
package code
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sitter "github.com/smacker/go-tree-sitter"
|
||||
)
|
||||
|
||||
type tsQueryMatchHandler func(*sitter.QueryMatch, *sitter.Query, bool) error
|
||||
|
||||
func TSExecQuery(query string, lang *sitter.Language, source []byte,
|
||||
node *sitter.Node, handler tsQueryMatchHandler) error {
|
||||
tsQuery, err := sitter.NewQuery([]byte(query), lang)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tsQueryCursor := sitter.NewQueryCursor()
|
||||
tsQueryCursor.Exec(tsQuery, node)
|
||||
|
||||
for {
|
||||
match, ok := tsQueryCursor.NextMatch()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
match = tsQueryCursor.FilterPredicates(match, source)
|
||||
|
||||
if len(match.Captures) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := handler(match, tsQuery, ok); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Maps a file path to a module name. This is an important operation
|
||||
// because it serves as the bridge between FS and Language domain
|
||||
func LangMapFileToModule(file SourceFile, repo SourceRepository, lang SourceLanguage, includeImports bool) (string, error) {
|
||||
// Get the relative path of the file in the repository because most language
|
||||
// runtimes (module loaders) identify modules by relative paths
|
||||
relSourceFilePath, err := repo.GetRelativePath(file.Path, includeImports)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get relative path: %w", err)
|
||||
}
|
||||
|
||||
// Use the language adapter to translate the relative path to a module name
|
||||
// which is language specific
|
||||
moduleName, err := lang.ResolveImportNameFromPath(relSourceFilePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to resolve import name from path: %w", err)
|
||||
}
|
||||
|
||||
return moduleName, nil
|
||||
}
|
||||
|
||||
// Maps a module name back to a file path that must exist in the repository
|
||||
func LangMapModuleToFile(moduleName string, currentFile SourceFile,
|
||||
repo SourceRepository, lang SourceLanguage, includeImports bool) (SourceFile, error) {
|
||||
// Use the language adapter to get possible relative paths for the module name
|
||||
relPaths, err := lang.ResolveImportPathsFromName(currentFile, moduleName, includeImports)
|
||||
if err != nil {
|
||||
return SourceFile{}, fmt.Errorf("failed to resolve import paths from name: %w", err)
|
||||
}
|
||||
|
||||
for _, relPath := range relPaths {
|
||||
sf, err := repo.GetSourceFileByPath(relPath, includeImports)
|
||||
if err != nil {
|
||||
continue
|
||||
} else {
|
||||
return sf, nil
|
||||
}
|
||||
}
|
||||
|
||||
return SourceFile{}, fmt.Errorf("no source file found for module: %s", moduleName)
|
||||
}
|
||||
20
pkg/code/utils_test.go
Normal file
20
pkg/code/utils_test.go
Normal file
@ -0,0 +1,20 @@
|
||||
package code
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestLangMapFileToModule(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
path string
|
||||
mod string
|
||||
err error
|
||||
}{
|
||||
{},
|
||||
}
|
||||
|
||||
for _, test := range cases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// TODO: Implement test
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,8 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/safedep/dry/utils"
|
||||
"github.com/safedep/vet/gen/insightapi"
|
||||
"github.com/safedep/vet/pkg/analyzer"
|
||||
"github.com/safedep/vet/pkg/common/logger"
|
||||
"github.com/safedep/vet/pkg/models"
|
||||
@ -29,6 +31,10 @@ type csvRecord struct {
|
||||
introducedBy string
|
||||
pathToRoot string
|
||||
violationReason string
|
||||
osvId string
|
||||
cveId string
|
||||
vulnSeverity string
|
||||
vulnSummary string
|
||||
}
|
||||
|
||||
func NewCsvReporter(config CsvReportingConfig) (Reporter, error) {
|
||||
@ -95,7 +101,8 @@ func (r *csvReporter) Finish() error {
|
||||
pathToRoot = strings.Join(pathPackages, " -> ")
|
||||
}
|
||||
|
||||
records = append(records, csvRecord{
|
||||
// Base record
|
||||
record := csvRecord{
|
||||
ecosystem: string(v.Package.Ecosystem),
|
||||
manifestPath: v.Manifest.GetDisplayPath(),
|
||||
packageName: v.Package.GetName(),
|
||||
@ -103,7 +110,49 @@ func (r *csvReporter) Finish() error {
|
||||
violationReason: msg,
|
||||
introducedBy: introducedBy,
|
||||
pathToRoot: pathToRoot,
|
||||
})
|
||||
}
|
||||
|
||||
// Flatten the vulnerabilities
|
||||
insight := utils.SafelyGetValue(v.Package.Insights)
|
||||
vulnerabilities := utils.SafelyGetValue(insight.Vulnerabilities)
|
||||
|
||||
// If no vulnerabilities, add the record as is and continue
|
||||
if len(vulnerabilities) == 0 {
|
||||
records = append(records, record)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, vuln := range vulnerabilities {
|
||||
vulnId := utils.SafelyGetValue(vuln.Id)
|
||||
aliases := utils.SafelyGetValue(vuln.Aliases)
|
||||
summary := utils.SafelyGetValue(vuln.Summary)
|
||||
|
||||
cveId := ""
|
||||
for _, alias := range aliases {
|
||||
if strings.HasPrefix(alias, "CVE-") {
|
||||
cveId = alias
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
risk := ""
|
||||
severities := utils.SafelyGetValue(vuln.Severities)
|
||||
for _, severity := range severities {
|
||||
sevType := utils.SafelyGetValue(severity.Type)
|
||||
if sevType == insightapi.PackageVulnerabilitySeveritiesTypeCVSSV2 || sevType == insightapi.PackageVulnerabilitySeveritiesTypeCVSSV3 {
|
||||
risk = string(utils.SafelyGetValue(severity.Risk))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
newRecord := record
|
||||
newRecord.osvId = vulnId
|
||||
newRecord.cveId = cveId
|
||||
newRecord.vulnSummary = summary
|
||||
newRecord.vulnSeverity = risk
|
||||
|
||||
records = append(records, newRecord)
|
||||
}
|
||||
}
|
||||
|
||||
err := r.persistCsvRecords(records)
|
||||
@ -131,7 +180,12 @@ func (r *csvReporter) persistCsvRecords(records []csvRecord) error {
|
||||
"Package Version",
|
||||
"Violation",
|
||||
"Introduced By",
|
||||
"Path To Root"})
|
||||
"Path To Root",
|
||||
"OSV ID",
|
||||
"CVE ID",
|
||||
"Vulnerability Severity",
|
||||
"Vulnerability Summary",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -143,6 +197,10 @@ func (r *csvReporter) persistCsvRecords(records []csvRecord) error {
|
||||
csvRecord.violationReason,
|
||||
csvRecord.introducedBy,
|
||||
csvRecord.pathToRoot,
|
||||
csvRecord.osvId,
|
||||
csvRecord.cveId,
|
||||
csvRecord.vulnSeverity,
|
||||
csvRecord.vulnSummary,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
68
pkg/storage/graph/graph.go
Normal file
68
pkg/storage/graph/graph.go
Normal file
@ -0,0 +1,68 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// A node in a property graph
|
||||
type Node struct {
|
||||
// Unique ID for the node.
|
||||
// The consumer of the graph is responsible for ensuring uniqueness.
|
||||
ID string
|
||||
|
||||
// Label for the node. This is usually the type of the node.
|
||||
// In a quad store, this would be the context of the quad.
|
||||
Label string
|
||||
|
||||
// Properties for the node.
|
||||
Properties map[string]string
|
||||
}
|
||||
|
||||
// An edge in a property graph
|
||||
type Edge struct {
|
||||
// Identifier for the type of relationship.
|
||||
Name string
|
||||
|
||||
// The label for the edge. This is usually the type of the edge.
|
||||
// In a quad store, this would be the context of the quad.
|
||||
Label string
|
||||
|
||||
// Properties for the edge.
|
||||
Properties map[string]string
|
||||
|
||||
// The node the edge is coming from.
|
||||
From *Node
|
||||
|
||||
// The node the edge is going to.
|
||||
To *Node
|
||||
}
|
||||
|
||||
// The result of a query on the graph. The results depends
|
||||
// on the implementation specific query executed on the graph
|
||||
type QueryResult interface {
|
||||
// Return the results as a slice of strings
|
||||
Strings() ([]string, error)
|
||||
|
||||
// Return the results as list of nodes
|
||||
// that matches a path query
|
||||
Nodes() ([]Node, error)
|
||||
}
|
||||
|
||||
type Graph interface {
|
||||
// Create a link in the graph between two Node using the Edge
|
||||
Link(link *Edge) error
|
||||
|
||||
// Query the graph using an implementation-specific query language
|
||||
Query(ctx context.Context, query string) (QueryResult, error)
|
||||
|
||||
// Close the graph
|
||||
Close() error
|
||||
}
|
||||
|
||||
// Configuration for property graph implementations that
|
||||
// uses local file system as the storage
|
||||
type LocalPropertyGraphConfig struct {
|
||||
Name string
|
||||
DatabasePath string
|
||||
OpenExisting bool
|
||||
}
|
||||
230
pkg/storage/graph/graph_cayley.go
Normal file
230
pkg/storage/graph/graph_cayley.go
Normal file
@ -0,0 +1,230 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
_ "github.com/cayleygraph/cayley/graph/kv/bolt"
|
||||
|
||||
"github.com/cayleygraph/cayley"
|
||||
"github.com/cayleygraph/cayley/graph"
|
||||
"github.com/cayleygraph/cayley/query"
|
||||
"github.com/cayleygraph/cayley/query/gizmo"
|
||||
"github.com/cayleygraph/quad"
|
||||
)
|
||||
|
||||
const (
|
||||
quadGraphVersion = "1.0.0"
|
||||
internalMetaContext = "internal_meta"
|
||||
queryMaxLimit = 10000
|
||||
)
|
||||
|
||||
type cayleyGraph struct {
|
||||
store *cayley.Handle
|
||||
config *LocalPropertyGraphConfig
|
||||
gizmoQuerySession *gizmo.Session
|
||||
}
|
||||
|
||||
func NewInMemoryPropertyGraph(config *LocalPropertyGraphConfig) (Graph, error) {
|
||||
store, err := cayley.NewMemoryGraph()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buildCayleyGraph(config, store)
|
||||
}
|
||||
|
||||
func NewPropertyGraph(config *LocalPropertyGraphConfig) (Graph, error) {
|
||||
var err error
|
||||
|
||||
if !config.OpenExisting {
|
||||
err = graph.InitQuadStore("bolt", config.DatabasePath, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize quad store: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Placeholder for future options
|
||||
options := graph.Options{}
|
||||
|
||||
store, err := cayley.NewGraph("bolt", config.DatabasePath, options)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create graph: %v", err)
|
||||
}
|
||||
|
||||
return buildCayleyGraph(config, store)
|
||||
}
|
||||
|
||||
func buildCayleyGraph(config *LocalPropertyGraphConfig, store *cayley.Handle) (*cayleyGraph, error) {
|
||||
err := store.AddQuad(quad.Make("_graph", "version", quadGraphVersion, internalMetaContext))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to add graph version: %v", err)
|
||||
}
|
||||
|
||||
return &cayleyGraph{
|
||||
config: config,
|
||||
store: store,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create the data model in the graph by linking nodes, properties
|
||||
// and edges
|
||||
func (g *cayleyGraph) Link(link *Edge) error {
|
||||
err := g.addNodeProperties(link.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = g.addNodeProperties(link.To)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
edge := quad.Make(link.From.ID, link.Name, link.To.ID, link.Label)
|
||||
err = g.store.AddQuad(edge)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for key, value := range link.Properties {
|
||||
err = g.store.AddQuad(quad.Make(edge, key, value, link.Label))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type cayleyQueryResult struct {
|
||||
store *cayley.Handle
|
||||
results []*gizmo.Result
|
||||
}
|
||||
|
||||
func (q *cayleyQueryResult) Strings() ([]string, error) {
|
||||
res := []string{}
|
||||
for _, r := range q.results {
|
||||
if r.Val == nil {
|
||||
for _, ref := range r.Tags {
|
||||
if ref == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
n := q.store.NameOf(ref)
|
||||
if n == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// https://github.com/cayleygraph/cayley/blob/master/query/gizmo/finals.go#L205
|
||||
if s, ok := n.(quad.String); ok {
|
||||
res = append(res, string(s))
|
||||
} else {
|
||||
res = append(res, quad.StringOf(n))
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
s, ok := r.Val.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected result type: %T", r.Result)
|
||||
}
|
||||
|
||||
res = append(res, s)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Reconstruct the nodes from the query results
|
||||
func (q *cayleyQueryResult) Nodes() ([]Node, error) {
|
||||
nodes := []Node{}
|
||||
for _, r := range q.results {
|
||||
if r.Val == nil {
|
||||
node := Node{Properties: map[string]string{}}
|
||||
for tag, ref := range r.Tags {
|
||||
if ref == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
n := q.store.NameOf(ref)
|
||||
if n == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
node.Properties[tag] = quad.StringOf(n)
|
||||
}
|
||||
} else {
|
||||
if s, ok := r.Val.(string); ok {
|
||||
node := Node{ID: s}
|
||||
nodes = append(nodes, node)
|
||||
} else {
|
||||
return nil, fmt.Errorf("unexpected result type: %T", r.Val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
// Query using Gizmo API
|
||||
// https://cayley.gitbook.io/cayley/query-languages/gizmoapi
|
||||
func (g *cayleyGraph) Query(ctx context.Context, q string) (QueryResult, error) {
|
||||
session := g.querySession()
|
||||
|
||||
it, err := session.Execute(ctx, q, query.Options{
|
||||
Collation: query.Raw,
|
||||
Limit: queryMaxLimit,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer it.Close()
|
||||
|
||||
qs := &cayleyQueryResult{store: g.store}
|
||||
for it.Next(ctx) {
|
||||
err := it.Err()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
// https://github.com/cayleygraph/cayley/blob/master/query/gizmo/gizmo_test.go#L710
|
||||
data, ok := it.Result().(*gizmo.Result)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected result type: %T", it.Result())
|
||||
}
|
||||
|
||||
qs.results = append(qs.results, data)
|
||||
}
|
||||
|
||||
return qs, err
|
||||
}
|
||||
|
||||
func (g *cayleyGraph) Close() error {
|
||||
return g.store.Close()
|
||||
}
|
||||
|
||||
func (g *cayleyGraph) addNodeProperties(node *Node) error {
|
||||
for key, value := range node.Properties {
|
||||
err := g.addProperty(node, key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *cayleyGraph) addProperty(node *Node, key, value string) error {
|
||||
return g.store.AddQuad(quad.Make(node.ID, key, value, node.Label))
|
||||
}
|
||||
|
||||
func (g *cayleyGraph) querySession() *gizmo.Session {
|
||||
if g.gizmoQuerySession == nil {
|
||||
g.gizmoQuerySession = gizmo.NewSession(g.store)
|
||||
}
|
||||
|
||||
return g.gizmoQuerySession
|
||||
}
|
||||
96
pkg/storage/graph/graph_cayley_test.go
Normal file
96
pkg/storage/graph/graph_cayley_test.go
Normal file
@ -0,0 +1,96 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCayleyGraphQuery(t *testing.T) {
|
||||
graphModelLinks := []Edge{
|
||||
{
|
||||
Name: "knows",
|
||||
From: &Node{
|
||||
ID: "alice",
|
||||
Label: "person",
|
||||
Properties: map[string]string{"name": "Alice"},
|
||||
},
|
||||
To: &Node{
|
||||
ID: "bob",
|
||||
Label: "person",
|
||||
Properties: map[string]string{"name": "Bob"},
|
||||
},
|
||||
Properties: map[string]string{"since": "2010"},
|
||||
},
|
||||
{
|
||||
Name: "knows",
|
||||
From: &Node{
|
||||
ID: "alice",
|
||||
Label: "person",
|
||||
Properties: map[string]string{"name": "Alice"},
|
||||
},
|
||||
To: &Node{
|
||||
ID: "charlie",
|
||||
Label: "person",
|
||||
Properties: map[string]string{"name": "Charlie"},
|
||||
},
|
||||
Properties: map[string]string{"since": "2012"},
|
||||
},
|
||||
{
|
||||
Name: "knows",
|
||||
From: &Node{
|
||||
ID: "bob",
|
||||
Label: "person",
|
||||
Properties: map[string]string{"name": "Bob"},
|
||||
},
|
||||
To: &Node{
|
||||
ID: "dexter",
|
||||
Label: "person",
|
||||
Properties: map[string]string{"name": "Dexter"},
|
||||
},
|
||||
Properties: map[string]string{"since": "2014"},
|
||||
},
|
||||
}
|
||||
|
||||
config := &LocalPropertyGraphConfig{
|
||||
Name: "test",
|
||||
DatabasePath: "test.db",
|
||||
}
|
||||
|
||||
g, err := NewInMemoryPropertyGraph(config)
|
||||
assert.Nil(t, err)
|
||||
|
||||
defer g.Close()
|
||||
|
||||
for _, link := range graphModelLinks {
|
||||
err = g.Link(&link)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
// Alice knows Bob and Charlie
|
||||
query := `
|
||||
g.V("alice").Out("knows").All()
|
||||
`
|
||||
|
||||
result, err := g.Query(context.TODO(), query)
|
||||
assert.Nil(t, err)
|
||||
|
||||
strings, err := result.Strings()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.ElementsMatch(t, []string{"bob", "charlie"}, strings)
|
||||
|
||||
// Alice do not know Dexter
|
||||
query = `
|
||||
g.V("alice").Out("knows").Has("name", "Dexter").All()
|
||||
`
|
||||
|
||||
result, err = g.Query(context.TODO(), query)
|
||||
assert.Nil(t, err)
|
||||
|
||||
strings, err = result.Strings()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Empty(t, strings)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user