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

Refactor the cmd for custom #132

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion api/v1/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func TestGetNodesByGlob(t *testing.T) {
func TestQueriesIngestAndCache(t *testing.T) {
s := setupService()

result, err := ingest.LoadDataFromPath(s.storage, "../../testdata/osv-sboms/google_agi.sbom.json")
result, err := ingest.LoadDataFromPath("../../testdata/osv-sboms/google_agi.sbom.json")
require.NoError(t, err)
for _, data := range result {
sbomReq := connect.NewRequest(&service.IngestSBOMRequest{
Expand Down
9 changes: 4 additions & 5 deletions cmd/ingest/ingest.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"github.com/bitbomdev/minefield/cmd/ingest/osv"
"github.com/bitbomdev/minefield/cmd/ingest/sbom"
"github.com/bitbomdev/minefield/cmd/ingest/scorecard"
"github.com/bitbomdev/minefield/pkg/graph"
"github.com/spf13/cobra"
)

Expand All @@ -13,16 +12,16 @@
func (o *options) AddFlags(_ *cobra.Command) {
}

func New(storage graph.Storage) *cobra.Command {
func New() *cobra.Command {

Check warning on line 15 in cmd/ingest/ingest.go

View check run for this annotation

Codecov / codecov/patch

cmd/ingest/ingest.go#L15

Added line #L15 was not covered by tests
cmd := &cobra.Command{
Use: "ingest",
Short: "ingest metadata into the graph",
SilenceUsage: true,
DisableAutoGenTag: true,
}

cmd.AddCommand(osv.New(storage))
cmd.AddCommand(sbom.New(storage))
cmd.AddCommand(scorecard.New(storage))
cmd.AddCommand(osv.New())
cmd.AddCommand(sbom.New())
cmd.AddCommand(scorecard.New())

Check warning on line 25 in cmd/ingest/ingest.go

View check run for this annotation

Codecov / codecov/patch

cmd/ingest/ingest.go#L23-L25

Added lines #L23 - L25 were not covered by tests
return cmd
}
12 changes: 4 additions & 8 deletions cmd/ingest/osv/osv.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@
"connectrpc.com/connect"
apiv1 "github.com/bitbomdev/minefield/gen/api/v1"
"github.com/bitbomdev/minefield/gen/api/v1/apiv1connect"
"github.com/bitbomdev/minefield/pkg/graph"
"github.com/bitbomdev/minefield/pkg/tools"
"github.com/bitbomdev/minefield/pkg/tools/ingest"
"github.com/spf13/cobra"
)

type options struct {
storage graph.Storage
addr string // Address of the minefield server
addr string // Address of the minefield server

ingestServiceClient apiv1connect.IngestServiceClient
}
Expand All @@ -37,7 +35,7 @@
}
vulnsPath := args[0]
// Ingest vulnerabilities
result, err := ingest.LoadDataFromPath(o.storage, vulnsPath)
result, err := ingest.LoadDataFromPath(vulnsPath)

Check warning on line 38 in cmd/ingest/osv/osv.go

View check run for this annotation

Codecov / codecov/patch

cmd/ingest/osv/osv.go#L38

Added line #L38 was not covered by tests
if err != nil {
return fmt.Errorf("failed to load vulnerabilities: %w", err)
}
Expand All @@ -56,10 +54,8 @@
return nil
}

func New(storage graph.Storage) *cobra.Command {
o := &options{
storage: storage,
}
func New() *cobra.Command {
o := &options{}

Check warning on line 58 in cmd/ingest/osv/osv.go

View check run for this annotation

Codecov / codecov/patch

cmd/ingest/osv/osv.go#L57-L58

Added lines #L57 - L58 were not covered by tests
cmd := &cobra.Command{
Use: "osv [path to vulnerability file/dir]",
Short: "Graph vulnerability data into the graph, and connect it to existing library nodes",
Expand Down
12 changes: 4 additions & 8 deletions cmd/ingest/sbom/sbom.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@
"connectrpc.com/connect"
apiv1 "github.com/bitbomdev/minefield/gen/api/v1"
"github.com/bitbomdev/minefield/gen/api/v1/apiv1connect"
"github.com/bitbomdev/minefield/pkg/graph"
"github.com/bitbomdev/minefield/pkg/tools"
"github.com/bitbomdev/minefield/pkg/tools/ingest"
"github.com/spf13/cobra"
)

type options struct {
storage graph.Storage
addr string // Address of the minefield server
addr string // Address of the minefield server

ingestServiceClient apiv1connect.IngestServiceClient
}
Expand All @@ -38,7 +36,7 @@
}
sbomPath := args[0]
// Ingest SBOM
result, err := ingest.LoadDataFromPath(o.storage, sbomPath)
result, err := ingest.LoadDataFromPath(sbomPath)

Check warning on line 39 in cmd/ingest/sbom/sbom.go

View check run for this annotation

Codecov / codecov/patch

cmd/ingest/sbom/sbom.go#L39

Added line #L39 was not covered by tests
if err != nil {
return fmt.Errorf("failed to ingest SBOM: %w", err)
}
Expand All @@ -59,10 +57,8 @@
return nil
}

func New(storage graph.Storage) *cobra.Command {
o := &options{
storage: storage,
}
func New() *cobra.Command {
o := &options{}

Check warning on line 61 in cmd/ingest/sbom/sbom.go

View check run for this annotation

Codecov / codecov/patch

cmd/ingest/sbom/sbom.go#L60-L61

Added lines #L60 - L61 were not covered by tests
cmd := &cobra.Command{
Use: "sbom [path to sbom file/dir]",
Short: "Ingest an sbom into the graph ",
Expand Down
14 changes: 5 additions & 9 deletions cmd/ingest/scorecard/scorecard.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@
"net/http"

"connectrpc.com/connect"
"github.com/bitbomdev/minefield/gen/api/v1"
apiv1 "github.com/bitbomdev/minefield/gen/api/v1"
"github.com/bitbomdev/minefield/gen/api/v1/apiv1connect"
"github.com/bitbomdev/minefield/pkg/graph"
"github.com/bitbomdev/minefield/pkg/tools"
"github.com/bitbomdev/minefield/pkg/tools/ingest"
"github.com/spf13/cobra"
)

type options struct {
storage graph.Storage
addr string // Address of the minefield server
addr string // Address of the minefield server

ingestServiceClient apiv1connect.IngestServiceClient
}
Expand All @@ -38,7 +36,7 @@
}
scorecardPath := args[0]

result, err := ingest.LoadDataFromPath(o.storage, scorecardPath)
result, err := ingest.LoadDataFromPath(scorecardPath)

Check warning on line 39 in cmd/ingest/scorecard/scorecard.go

View check run for this annotation

Codecov / codecov/patch

cmd/ingest/scorecard/scorecard.go#L39

Added line #L39 was not covered by tests
if err != nil {
return fmt.Errorf("failed to ingest SBOM: %w", err)
}
Expand All @@ -59,10 +57,8 @@
return nil
}

func New(storage graph.Storage) *cobra.Command {
o := &options{
storage: storage,
}
func New() *cobra.Command {
o := &options{}

Check warning on line 61 in cmd/ingest/scorecard/scorecard.go

View check run for this annotation

Codecov / codecov/patch

cmd/ingest/scorecard/scorecard.go#L60-L61

Added lines #L60 - L61 were not covered by tests
cmd := &cobra.Command{
Use: "scorecard [path to scorecard file/dir]",
Short: "Graph scorecard data into the graph, and connect it to existing library nodes",
Expand Down
3 changes: 1 addition & 2 deletions cmd/leaderboard/leaderboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
import (
"github.com/bitbomdev/minefield/cmd/leaderboard/custom"
"github.com/bitbomdev/minefield/cmd/leaderboard/keys"
"github.com/bitbomdev/minefield/pkg/graph"
"github.com/spf13/cobra"
)

type options struct{}

func (o *options) AddFlags(_ *cobra.Command) {}

func New(storage graph.Storage) *cobra.Command {
func New() *cobra.Command {

Check warning on line 13 in cmd/leaderboard/leaderboard.go

View check run for this annotation

Codecov / codecov/patch

cmd/leaderboard/leaderboard.go#L13

Added line #L13 was not covered by tests
o := &options{}
cmd := &cobra.Command{
Use: "leaderboard",
Expand Down
142 changes: 59 additions & 83 deletions cmd/query/custom/custom.go
Original file line number Diff line number Diff line change
@@ -1,157 +1,133 @@
package custom

import (
"bufio"
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"

"connectrpc.com/connect"
"github.com/RoaringBitmap/roaring"
"github.com/bitbomdev/minefield/cmd/helpers"
apiv1 "github.com/bitbomdev/minefield/gen/api/v1"
"github.com/bitbomdev/minefield/gen/api/v1/apiv1connect"
"github.com/bitbomdev/minefield/pkg/graph"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
)

// options holds the command-line options.
type options struct {
storage graph.Storage
visualize bool
visualizerAddr string
maxOutput int
showInfo bool
saveQuery string
maxOutput int
showInfo bool
saveQuery string
addr string
output string
queryServiceClient apiv1connect.QueryServiceClient
}

// AddFlags adds command-line flags to the provided cobra command.
func (o *options) AddFlags(cmd *cobra.Command) {
cmd.Flags().IntVar(&o.maxOutput, "max-getMetadata", 10, "max getMetadata length")
cmd.Flags().BoolVar(&o.visualize, "visualize", false, "visualize the query")
cmd.Flags().StringVar(&o.visualizerAddr, "addr", "8081", "address to run the visualizer on")
cmd.Flags().IntVar(&o.maxOutput, "max-output", 10, "maximum number of results to display")
cmd.Flags().BoolVar(&o.showInfo, "show-info", true, "display the info column")
cmd.Flags().StringVar(&o.saveQuery, "save-query", "", "save the query to a specific file")
cmd.Flags().StringVar(&o.addr, "addr", "http://localhost:8089", "address of the minefield server")
cmd.Flags().StringVar(&o.output, "output", "table", "output format (table or json)")
}

// Run executes the custom command with the provided arguments.
func (o *options) Run(cmd *cobra.Command, args []string) error {
script := strings.Join(args, " ")

if strings.TrimSpace(script) == "" {
return fmt.Errorf("script cannot be empty")
}
httpClient := &http.Client{}
addr := os.Getenv("BITBOMDEV_ADDR")
if addr == "" {
addr = "http://localhost:8089"

// Initialize client if not injected (for testing)
if o.queryServiceClient == nil {
o.queryServiceClient = apiv1connect.NewQueryServiceClient(
http.DefaultClient,
o.addr,
connect.WithGRPC(),
connect.WithSendGzip(),
)

Check warning on line 50 in cmd/query/custom/custom.go

View check run for this annotation

Codecov / codecov/patch

cmd/query/custom/custom.go#L45-L50

Added lines #L45 - L50 were not covered by tests
}
client := apiv1connect.NewQueryServiceClient(httpClient, addr)

// Create a new context
ctx := cmd.Context()

// Create a new QueryRequest
req := connect.NewRequest(&apiv1.QueryRequest{
Script: script,
})

// Make the Query request
res, err := client.Query(ctx, req)
res, err := o.queryServiceClient.Query(ctx, req)
if err != nil {
return fmt.Errorf("query failed: %v", err)
}

// Initialize the table
table := tablewriter.NewWriter(os.Stdout)
table.SetAutoWrapText(false)
table.SetRowLine(true)
if len(res.Msg.Nodes) == 0 {
return fmt.Errorf("no nodes found for script: %s", script)
}

// Dynamically set the header based on the showInfo flag
switch o.output {
case "json":
jsonOutput, err := helpers.FormatNodeJSON(res.Msg.Nodes)
if err != nil {
return fmt.Errorf("failed to format nodes as JSON: %w", err)
}

Check warning on line 72 in cmd/query/custom/custom.go

View check run for this annotation

Codecov / codecov/patch

cmd/query/custom/custom.go#L71-L72

Added lines #L71 - L72 were not covered by tests
cmd.Println(string(jsonOutput))
return nil
case "table":
return formatTable(cmd.OutOrStdout(), res.Msg.Nodes, o.maxOutput, o.showInfo)
default:
return fmt.Errorf("unknown output format: %s", o.output)
}
}

// formatTable formats the nodes into a table and writes it to the provided writer.
func formatTable(w io.Writer, nodes []*apiv1.Node, maxOutput int, showInfo bool) error {
table := tablewriter.NewWriter(w)
headers := []string{"Name", "Type", "ID"}
if o.showInfo {
if showInfo {
headers = append(headers, "Info")
}
table.SetHeader(headers)
table.SetAutoWrapText(false)
table.SetRowLine(true)

// Build the rows
count := 0
var f *os.File
if o.saveQuery != "" {
f, err = os.Create(o.saveQuery)
if err != nil {
return err
}
defer f.Close()
}
for _, node := range res.Msg.Nodes {
if count >= o.maxOutput {
for _, node := range nodes {
if count >= maxOutput {
break
}

// Build the common row data
row := []string{
node.Name,
node.Type,
strconv.Itoa(int(node.Id)),
strconv.FormatUint(uint64(node.Id), 10),
}

// If showInfo is true, compute the additionalInfo and append it
if o.showInfo {
if showInfo {
additionalInfo := helpers.ComputeAdditionalInfo(node)
row = append(row, additionalInfo)
}

// Append the row to the table
table.Append(row)

if o.saveQuery != "" {
f.WriteString(node.Name + "\n")
}
count++
}

// Render the table
table.Render()

// Visualization logic (remaining the same)
if o.visualize {
server := &http.Server{
Addr: ":" + o.visualizerAddr,
}

ids := roaring.New()

for _, node := range res.Msg.Nodes {
ids.Add(node.Id)
}

shutdown, err := graph.RunGraphVisualizer(o.storage, ids, script, server)
if err != nil {
return err
}
defer shutdown()

fmt.Println("Press Enter to stop the server and continue...")
if _, err := bufio.NewReader(os.Stdin).ReadBytes('\n'); err != nil {
return err
}
}

return nil
}

func New(storage graph.Storage) *cobra.Command {
o := &options{
storage: storage,
}
// New creates and returns a new Cobra command for executing custom query scripts.
func New() *cobra.Command {
o := &options{}

cmd := &cobra.Command{
Use: "custom [script]",
Short: "Query dependencies and dependents of a project",
Args: cobra.MinimumNArgs(1),
Short: "Execute a custom query script",
Long: "Execute a custom query script to perform tailored queries against the project's dependencies and dependents.",
Args: cobra.ExactArgs(1),
RunE: o.Run,
DisableAutoGenTag: true,
}

o.AddFlags(cmd)

return cmd
Expand Down
Loading
Loading