Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Analyze command to guacone #1809

Open
wants to merge 66 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
70a37cd
adding files
arorasoham9 Apr 4, 2024
0b56fe0
fixing mod sum
arorasoham9 Apr 4, 2024
62f2fa5
lint fixes
arorasoham9 Apr 4, 2024
a36daed
lint fixes
arorasoham9 Apr 4, 2024
9689179
mod fixes
arorasoham9 Apr 4, 2024
0bc691b
fix cli name
arorasoham9 Apr 4, 2024
d7f7503
changes to work with test cases
arorasoham9 Apr 5, 2024
728dfd7
adding tests and more fixes in analyze.go to support
arorasoham9 Apr 5, 2024
096894c
fix for highlight analysis
arorasoham9 Apr 5, 2024
9a0c88e
changes requested
arorasoham9 Apr 10, 2024
2ab452f
lint and few changes here and there
arorasoham9 Apr 10, 2024
ccd8d89
missed lint fixes, file rename to match pkg
arorasoham9 Apr 10, 2024
dffd603
removing dup files
arorasoham9 Apr 10, 2024
a6a278c
formatting
arorasoham9 Apr 10, 2024
8e33c9e
bug fixes
arorasoham9 Apr 11, 2024
16b7060
redundant check
arorasoham9 Apr 11, 2024
f527d22
test
arorasoham9 Apr 22, 2024
3570aac
major changes
arorasoham9 Apr 29, 2024
f732755
fixes to analyze command
arorasoham9 Apr 29, 2024
2a68d97
fixes to analyze command
arorasoham9 Apr 29, 2024
dc55196
mod sum changes
arorasoham9 Apr 29, 2024
4624aa9
fixes to output
arorasoham9 May 16, 2024
0337623
analyzer changes
arorasoham9 May 17, 2024
384befb
fixing test
arorasoham9 May 17, 2024
11f1c03
removing unncessary structs and fixing filename
arorasoham9 May 17, 2024
f6e9980
fix graph test case
arorasoham9 May 20, 2024
6d6d8cb
fix graph paths test case
arorasoham9 May 20, 2024
c1fb873
fix graph paths test case
arorasoham9 May 20, 2024
9a9d8cc
fix graph paths test case
arorasoham9 May 20, 2024
744dbe8
clubbed test cases
arorasoham9 May 20, 2024
806a4bf
int overflow fix w / tilt
arorasoham9 May 20, 2024
0675da5
Merge branch 'guacsec:main' into guacdiff
arorasoham9 May 23, 2024
a2bafee
removing unncessary files
arorasoham9 May 24, 2024
947adc8
removing unncessary files
arorasoham9 May 24, 2024
8949075
removing test JSON file
arorasoham9 May 24, 2024
7682412
Merge branch 'guacsec:main' into guacdiff
arorasoham9 May 24, 2024
57c1b2c
Merge branch 'guacsec:main' into guacdiff
arorasoham9 May 30, 2024
f4b5590
changes requested
arorasoham9 Jun 24, 2024
167f6f1
moving flag init
arorasoham9 Jul 5, 2024
067803b
moving print functions to new file, fixing lint
arorasoham9 Jul 5, 2024
df6d68a
moving print functions to new file, fixing lint
arorasoham9 Jul 5, 2024
2505185
moving print functions to new file, removing interface{} type in Node
arorasoham9 Jul 5, 2024
c8f6392
removing hardcoded values
arorasoham9 Jul 5, 2024
c04e02c
fixing flag names
arorasoham9 Jul 8, 2024
dd2ebb7
fixed repetitive path difference detection
arorasoham9 Jul 12, 2024
5795cfc
added TODO for bug where multiple paths may have equal differences wi…
arorasoham9 Jul 12, 2024
5bc9ed3
diffpatchmatch output for guacdiff
arorasoham9 Jul 26, 2024
24b2ac5
removing edges from nodes
arorasoham9 Aug 11, 2024
b217cec
Merge branch 'guacsec:main' into guacdiff
arorasoham9 Aug 11, 2024
a01cd3e
print fixes and mod
arorasoham9 Aug 11, 2024
4092bce
fixing output to show diffpatchmatch
arorasoham9 Aug 13, 2024
d7f2658
print format
arorasoham9 Aug 15, 2024
bd1f6fa
lint
arorasoham9 Aug 15, 2024
e334803
lint
arorasoham9 Aug 15, 2024
b85e676
Merge branch 'main' into guacdiff
arorasoham9 Aug 15, 2024
1bbb1b1
mod
arorasoham9 Aug 15, 2024
81c653c
implementing termui
arorasoham9 Sep 19, 2024
f718aa8
changing tablewriter
arorasoham9 Sep 20, 2024
17d5cf4
Merge branch 'main' into guacdiff
arorasoham9 Nov 18, 2024
08986ef
some more term ui fixes
arorasoham9 Nov 18, 2024
446e327
fixing multi line output, line wrapping and interactive ui
arorasoham9 Nov 19, 2024
d90d0ec
Merge branch 'guacsec:main' into guacdiff
arorasoham9 Nov 19, 2024
cf714a3
clean up
arorasoham9 Nov 19, 2024
d9f3e5f
Merge branch 'main' into guacdiff
arorasoham9 Nov 20, 2024
dd28a26
Merge branch 'main' into guacdiff
arorasoham9 Dec 4, 2024
c24277a
fixing mod
arorasoham9 Dec 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
266 changes: 266 additions & 0 deletions cmd/guacone/cmd/analyze.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
//
// Copyright 2024 The GUAC Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"context"
"fmt"
"net/http"
"os"

"github.com/Khan/genqlient/graphql"
"github.com/dominikbraun/graph"
analyzer "github.com/guacsec/guac/pkg/analyzer"
model "github.com/guacsec/guac/pkg/assembler/clients/generated"
"github.com/guacsec/guac/pkg/cli"
"github.com/guacsec/guac/pkg/logging"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

type AnalyzeOpts struct {
Metadata bool
InclSoft bool
InclDeps bool
InclOccur bool
Namespaces bool
URI bool
PURL bool
}

var analyzeCmd = &cobra.Command{
Use: "analyze <operation> <sboms> [flags] ",
Short: "analyze is a CLI tool tailored for comparing, intersecting, and merging Software Bill of Materials (SBOMs) within GUAC",
Long: `Diff Analysis: Compare two SBOMs to identify differences in their software components, versions, and dependencies.
Intersection Analysis: Determine the intersection of two SBOMs, highlighting common software components shared between them.
Union Analysis: Combine two SBOMs to create a unified SBOM, merging software component lists while maintaining version integrity.`,
Example: `
Ingest the SBOMs to analyze:
$ guacone collect files guac-data-main/docs/spdx/syft-spdx-k8s.gcr.io-kube-apiserver.v1.24.4.json
$ guacone collect files guac-data-main/docs/spdx/spdx_vuln.json

Difference
$ guacone analyze diff --analyze-uri-input --analyze-sboms=https://anchore.com/syft/image/ghcr.io/guacsec/vul-image-latest-6fd9de7b-9bec-4ae7-99d9-4b5e5ef6b869,https://anchore.com/syft/image/k8s.gcr.io/kube-apiserver-v1.24.4-b15339bc-a146-476e-a789-6a65e4e22e54

Union
$ guacone analyze union --analyze-uri-input --analyze-sboms=https://anchore.com/syft/image/ghcr.io/guacsec/vul-image-latest-6fd9de7b-9bec-4ae7-99d9-4b5e5ef6b869,https://anchore.com/syft/image/k8s.gcr.io/kube-apiserver-v1.24.4-b15339bc-a146-476e-a789-6a65e4e22e54

Intersection
$ guacone analyze intersect --analyze-uri-input --analyze-sboms=https://anchore.com/syft/image/ghcr.io/guacsec/vul-image-latest-6fd9de7b-9bec-4ae7-99d9-4b5e5ef6b869,https://anchore.com/syft/image/k8s.gcr.io/kube-apiserver-v1.24.4-b15339bc-a146-476e-a789-6a65e4e22e54
`,
Run: func(cmd *cobra.Command, args []string) {

if len(args) < 1 || len(args) > 1 {
fmt.Println("required 1 positional arguments, got", len(args))
os.Exit(1)
}

if args[0] != "intersect" && args[0] != "union" && args[0] != "diff" {
fmt.Println("invalid positional argument. Must be one of: intersect, union or diff.")
os.Exit(1)
}

ctx := logging.WithLogger(context.Background())
logger := logging.FromContext(ctx)
httpClient := http.Client{}
gqlclient := graphql.NewClient(viper.GetString("gql-addr"), &httpClient)

slsas := viper.GetStringSlice("analyze-slsa")
sboms := viper.GetStringSlice("analyze-sboms")
uri := viper.GetBool("analyze-uri-input")
purl := viper.GetBool("analyze-purl-input")

metadata := viper.GetBool("analyze-metadata")
inclSoft := viper.GetBool("analyze-incl-soft")
inclDeps := viper.GetBool("analyze-incl-deps")
inclOccur := viper.GetBool("analyze-incl-occur")
namespaces := viper.GetBool("analyze-namespaces")

var graphs []graph.Graph[string, *analyzer.Node]
var err error

if err = validateAnalyzeFlags(slsas, sboms, uri, purl); err != nil {
fmt.Fprintf(os.Stderr, "error: %s", err)
_ = cmd.Help()
os.Exit(1)
}

graphs, err = hasSBOMToGraph(ctx, gqlclient, sboms, AnalyzeOpts{
Metadata: metadata, InclSoft: inclSoft, InclDeps: inclDeps, InclOccur: inclOccur,
Namespaces: namespaces, URI: uri, PURL: purl})

if err != nil {
logger.Fatalf("Unable to generate graphs: %v", err)
}

if args[0] == "diff" {
gOne, gTwo, err := analyzer.CompressGraphs(graphs[0], graphs[1])
if err != nil {
logger.Fatalf("compress graphs fail: %v", err)
}

analysisOne, analysisTwo, err := analyzer.HighlightAnalysis(gOne, gTwo, analyzer.Difference)
if err != nil {
logger.Fatalf("unable to generate diff analysis: %v", err)
}

diffs, err := analyzer.CompareAllPaths(analysisOne, analysisTwo)
if err != nil {
logger.Fatalf("unable to generate diff analysis: %v", err)
}

if err = analyzer.PrintAnalysis(diffs); err != nil {
logger.Fatalf("unable to print diff analysis: %v", err)
}

} else if args[0] == "intersect" {
analysisOne, analysisTwo, err := analyzer.HighlightAnalysis(graphs[0], graphs[1], analyzer.Intersection)
if err != nil {
logger.Fatalf("Unable to generate intersect analysis: %v", err)
}
if err = analyzer.PrintPathTable("Common Paths", analysisOne, analysisTwo); err != nil {
logger.Fatalf("unable to print intersect analysis: %v", err)
}
} else if args[0] == "union" {
analysisOne, analysisTwo, err := analyzer.HighlightAnalysis(graphs[0], graphs[1], analyzer.Union)
if err != nil {
logger.Fatalf("unable to generate union analysis: %v", err)
}
if err = analyzer.PrintPathTable("All Paths", analysisOne, analysisTwo); err != nil {
logger.Fatalf("unable to print union analysis: %v", err)
}
}
},
}

func hasSBOMToGraph(ctx context.Context, gqlclient graphql.Client, sboms []string, opts AnalyzeOpts) ([]graph.Graph[string, *analyzer.Node], error) {

var hasSBOMResponseOne *model.HasSBOMsResponse
var hasSBOMResponseTwo *model.HasSBOMsResponse
var err error
logger := logging.FromContext(ctx)

if opts.URI {
hasSBOMResponseOne, err = analyzer.FindHasSBOMBy(model.HasSBOMSpec{}, sboms[0], "", "", ctx, gqlclient)
if err != nil {
return []graph.Graph[string, *analyzer.Node]{}, fmt.Errorf("(uri)failed to lookup sbom: %v %v", sboms[0], err)
}
hasSBOMResponseTwo, err = analyzer.FindHasSBOMBy(model.HasSBOMSpec{}, sboms[1], "", "", ctx, gqlclient)
if err != nil {
return []graph.Graph[string, *analyzer.Node]{}, fmt.Errorf("(uri)failed to lookup sbom: %v %v", sboms[1], err)
}
} else if opts.PURL {
hasSBOMResponseOne, err = analyzer.FindHasSBOMBy(model.HasSBOMSpec{}, "", sboms[0], "", ctx, gqlclient)
if err != nil {
return []graph.Graph[string, *analyzer.Node]{}, fmt.Errorf("(purl)failed to lookup sbom: %v %v", sboms[0], err)
}
hasSBOMResponseTwo, err = analyzer.FindHasSBOMBy(model.HasSBOMSpec{}, "", sboms[1], "", ctx, gqlclient)
if err != nil {
return []graph.Graph[string, *analyzer.Node]{}, fmt.Errorf("(purl)failed to lookup sbom: %v %v", sboms[1], err)
}
}

if hasSBOMResponseOne == nil || hasSBOMResponseTwo == nil {
return []graph.Graph[string, *analyzer.Node]{}, fmt.Errorf("failed to lookup sboms: nil")
}

if len(hasSBOMResponseOne.HasSBOM) == 0 || len(hasSBOMResponseTwo.HasSBOM) == 0 {
return []graph.Graph[string, *analyzer.Node]{}, fmt.Errorf("failed to lookup sboms, one endpoint may not have sboms")
}

if len(hasSBOMResponseOne.HasSBOM) != 1 || len(hasSBOMResponseTwo.HasSBOM) != 1 {
logger.Infof("multiple sboms found for given purl, id or uri. Using first one")
}
hasSBOMOne := hasSBOMResponseOne.HasSBOM[0]
hasSBOMTwo := hasSBOMResponseTwo.HasSBOM[0]

gOne, err := analyzer.MakeGraph(hasSBOMOne, opts.Metadata, opts.InclSoft, opts.InclDeps, opts.InclOccur, opts.Namespaces)
if err != nil {
logger.Fatalf(err.Error())
}
gTwo, err := analyzer.MakeGraph(hasSBOMTwo, opts.Metadata, opts.InclSoft, opts.InclDeps, opts.InclOccur, opts.Namespaces)
if err != nil {
logger.Fatalf(err.Error())
}
return []graph.Graph[string, *analyzer.Node]{
gOne,
gTwo,
}, nil
}

func validateAnalyzeFlags(slsas, sboms []string, uri, purl bool) error {

if len(slsas) == 0 && len(sboms) == 0 {
return fmt.Errorf("must specify slsa or sboms")
}

if len(slsas) > 0 && len(sboms) > 0 {
return fmt.Errorf("must either specify slsa or sbom")
}

if (len(slsas) <= 1 || len(slsas) > 2) && len(sboms) == 0 {
return fmt.Errorf("must specify exactly two slsas to analyze, specified %v", len(slsas))
}

if (len(sboms) <= 1 || len(sboms) > 2) && len(slsas) == 0 {
return fmt.Errorf("must specify exactly two sboms to analyze, specified %v", len(sboms))
}

if len(slsas) == 2 {
return fmt.Errorf("slsa diff to be implemented")
}

if sboms[0] == "" || sboms[1] == "" {
return fmt.Errorf("expected sbom received \"\"")
}

if !uri && !purl {
return fmt.Errorf("must provide one of --uri or --purl")
}

if uri && purl {
return fmt.Errorf("must provide only one of --uri or --purl")
}

return nil
}

func init() {
set, err := cli.BuildFlags([]string{
"analyze-sboms",
"analyze-slsa",
"analyze-uri-input",
"analyze-purl-input",
"analyze-id-input",
"analyze-metadata",
"analyze-incl-soft",
"analyze-incl-deps",
"analyze-incl-occur",
"analyze-namespaces",
})
if err != nil {
fmt.Fprintf(os.Stderr, "failed to setup flag: %v", err)
os.Exit(1)
}
analyzeCmd.PersistentFlags().AddFlagSet(set)
if err := viper.BindPFlags(analyzeCmd.PersistentFlags()); err != nil {
fmt.Fprintf(os.Stderr, "failed to bind flags: %v", err)
os.Exit(1)
}

rootCmd.AddCommand(analyzeCmd)

}
15 changes: 13 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/fsouza/fake-gcs-server v1.50.2
github.com/in-toto/in-toto-golang v0.9.0
github.com/neo4j/neo4j-go-driver/v4 v4.4.7
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.8.0
github.com/spf13/cobra v1.8.1
go.uber.org/zap v1.27.0
Expand Down Expand Up @@ -202,7 +203,6 @@ require (
github.com/nats-io/jwt/v2 v2.5.8 // indirect
github.com/nats-io/nkeys v0.4.7 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo/v2 v2.13.1 // indirect
github.com/onsi/gomega v1.29.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
Expand Down Expand Up @@ -232,7 +232,7 @@ require (
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa // indirect
github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
Expand Down Expand Up @@ -356,3 +356,14 @@ require (
golang.org/x/time v0.8.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/gdamore/encoding v1.0.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
)

require (
github.com/dominikbraun/graph v0.23.0
github.com/gdamore/tcell/v2 v2.7.4
github.com/rivo/tview v0.0.0-20241103174730-c76f7879f592
)
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqY
github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58=
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aTknUK9w6DHLDox2r2M3DI4i2pnd3w=
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q=
Expand Down Expand Up @@ -312,6 +314,10 @@ github.com/fsouza/fake-gcs-server v1.50.2 h1:ulrS1pavCOCbMZfN5ZPgBRMFWclON9xDsuL
github.com/fsouza/fake-gcs-server v1.50.2/go.mod h1:VU6Zgei4647KuT4XER8WHv5Hcj2NIySndyG8gfvwckA=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU=
github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg=
github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4=
github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM=
github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvpAv8=
Expand Down Expand Up @@ -562,6 +568,8 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA=
github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
Expand All @@ -576,6 +584,7 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
Expand Down Expand Up @@ -718,7 +727,10 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6O
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rhysd/actionlint v1.6.26 h1:zi7jPZf3Ks14gCXYAAL47uBziyFlX7+Xwilqhexct9g=
github.com/rhysd/actionlint v1.6.26/go.mod h1:TIj1DlCgtYLOv5CH9wCK+WJTOr1qAdnFzkGi0IgSCO4=
github.com/rivo/tview v0.0.0-20241103174730-c76f7879f592 h1:YIJ+B1hePP6AgynC5TcqpO0H9k3SSoZa2BGyL6vDUzM=
github.com/rivo/tview v0.0.0-20241103174730-c76f7879f592/go.mod h1:02iFIz7K/A9jGCvrizLPvoqr4cEIx7q54RH5Qudkrss=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
Expand Down Expand Up @@ -1034,6 +1046,7 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
Expand All @@ -1046,6 +1059,7 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
Loading
Loading