Format status json

This commit is contained in:
Alvaro Muñoz
2023-03-28 23:06:59 +02:00
parent 18e38d441e
commit 14c19d1654
2 changed files with 88 additions and 75 deletions

View File

@@ -5,30 +5,37 @@
## Configuration
A configuration file will be created in `~/.config/mrva/config.yml`. The following options are supported:
- `codeql-path`: Path to CodeQL distribution
- `controller`: NWO of the MRVA controller to use
- `listFile`: Path to the JSON file containing the target repos
## Usage
Until the extension gets published you can use `go run .` instead of `gh mrva`
### Submit a new query
```bash
gh mrva submit [--controller <CONTROLLER>] --lang <LANGUAGE> [--list-file <LISTFILE>] --list <LIST> --query <QUERY> [--name <NAME>]
gh mrva submit [--codeql-path<path to CodeQL>] [--controller <controller>] --lang <language> --name <run name> [--list-file <list file>] --list <list> [--query <query> | --query-suite <query suite> ]
```
Note: `controller` and `list-file` are only optionals if defined in the configuration file
Note: if a `name` (any arbitrary name) is provided, the resulting run IDs will be stored in the configuration file so they can be referenced later for download
Note: `codeql-dist`, `controller` and `list-file` are only optionals if defined in the configuration file
### Download the results
```bash
gh mrva download [--controller <CONTROLLER>] --lang <LANGUAGE> --output-dir <OUTPUTDIR> [--name <NAME> | --run <ID>] [--download-dbs]
gh mrva download --name <run name> --output-dir <output directory> [--download-dbs] [--nwo <owner/repo>]
```
Note: `controller` is only optionals if defined in the configuration file
Note: if a `name` is provided, the run ID is not necessary and instead `gh-mrva` will download the artifacts associated that `name` as found in the configuration file
### List sessions
```bash
gh mrva list [--json]
```
### Check scan status
```bash
gh mrva status --name <run name> [--json]
```
## Contributing

140
main.go
View File

@@ -10,7 +10,7 @@ import (
"fmt"
"github.com/cli/go-gh"
"github.com/cli/go-gh/pkg/api"
"github.com/cli/go-gh/pkg/jsonpretty"
// "github.com/cli/go-gh/pkg/jsonpretty"
"github.com/google/uuid"
"gopkg.in/yaml.v3"
"io/ioutil"
@@ -476,7 +476,7 @@ func downloadDatabase(nwo string, language string, outputDir string) error {
return nil
}
func saveInHistory(name string, controller string, runIds []int, language string, listFile string, list string, query string, count int) error {
func saveInHistory(name string, controller string, runs []Run, language string, listFile string, list string, query string, count int) error {
configData, err := getConfig(configFilePath)
if err != nil {
return err
@@ -490,13 +490,12 @@ func saveInHistory(name string, controller string, runIds []int, language string
} else {
configData.History[name] = HistoryEntry{
Name: name,
RunIds: runIds,
Runs: runs,
Timestamp: time.Now(),
Controller: controller,
Language: language,
ListFile: listFile,
List: list,
Query: query,
RepositoryCount: count,
}
}
@@ -513,14 +512,14 @@ func saveInHistory(name string, controller string, runIds []int, language string
return nil
}
func loadFromHistory(name string) (string, []int, string, error) {
func loadFromHistory(name string) (string, []Run, string, error) {
configData, err := getConfig(configFilePath)
if err != nil {
return "", nil, "", err
}
if configData.History != nil {
if entry, ok := configData.History[name]; ok {
return entry.Controller, entry.RunIds, entry.Language, nil
return entry.Controller, entry.Runs, entry.Language, nil
}
}
return "", nil, "", errors.New("No history entry found for " + name)
@@ -539,15 +538,19 @@ func getConfig(path string) (Config, error) {
return configData, nil
}
type Run struct {
Id int `yaml:"run_id"`
Query string `yaml:"query"`
}
type HistoryEntry struct {
Name string `yaml:"name"`
Timestamp time.Time `yaml:"timestamp"`
RunIds []int `yaml:"runIds"`
Runs []Run `yaml:"runIds"`
Controller string `yaml:"controller"`
ListFile string `yaml:"listFile"`
List string `yaml:"list"`
Language string `yaml:"language"`
Query string `yaml:"query"`
RepositoryCount int `yaml:"repositoryCount"`
}
type Config struct {
@@ -589,17 +592,16 @@ func main() {
helpFlag := flag.String("help", "", "This help documentation.")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, `
gh mrva - submit and download CodeQL queries from MRVA
fmt.Fprintf(os.Stderr, `gh mrva - Run CodeQL queries at scale using Multi-Repository Variant Analysis (MRVA)
Usage:
gh mrva submit [--codeql-dist <path to CodeQL dist>] [--controller <controller>] --lang <language> --name <run name> [--list-file <list file>] --list <list> [--query <query> | --query-suite <query suite>]
gh mrva submit [--codeql-path <path to CodeQL>] [--controller <controller>] --lang <language> --name <run name> [--list-file <list file>] --list <list> [--query <query> | --query-suite <query suite>]
gh mrva download --name <run name> --output-dir <output directory> [--download-dbs]
gh mrva download --name <run name> --output-dir <output directory> [--download-dbs] [--nwo <owner/repo>]
gh mrva status --name <run name> [--json]
gh mrva list [--json]
gh mrva list [--json]
`)
}
@@ -638,8 +640,7 @@ func status(args []string) {
jsonFlag := flag.Bool("json", false, "Output in JSON format (default: false)")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, `
gh mrva - submit and download CodeQL queries from MRVA
fmt.Fprintf(os.Stderr, `gh mrva - Run CodeQL queries at scale using Multi-Repository Variant Analysis (MRVA)
Usage:
gh mrva status --name <run name> [--json]
@@ -662,45 +663,47 @@ Usage:
os.Exit(1)
}
controller, runIds, _, err := loadFromHistory(runName)
controller, runs, _, err := loadFromHistory(runName)
if err != nil {
log.Fatal(err)
}
if len(runIds) == 0 {
if len(runs) == 0 {
log.Fatal("No runs found for run name", runName)
}
type Run struct {
Id int
Status string
FailureReason string
type RunStatus struct {
Id int `json:"id"`
Query string `json:"query"`
Status string `json:"status"`
FailureReason string `json:"failure_reason"`
}
type RepoWithFindings struct {
Nwo string
Count int
Nwo string `json:"nwo"`
Count int `json:"count"`
RunId int `json:"run_id"`
}
type Results struct {
Runs []Run
ResositoriesWithFindings []RepoWithFindings
TotalFindingsCount int
TotalSuccessfulScans int
TotalFailedScans int
TotalRepositoriesWithFindings int
TotalSkippedRepositories int
TotalSkippedAccessMismatchRepositories int
TotalSkippedNotFoundRepositories int
TotalSkippedNoDatabaseRepositories int
TotalSkippedOverLimitRepositories int
Runs []RunStatus `json:"runs"`
ResositoriesWithFindings []RepoWithFindings `json:"repositories_with_findings"`
TotalFindingsCount int `json:"total_findings_count"`
TotalSuccessfulScans int `json:"total_successful_scans"`
TotalFailedScans int `json:"total_failed_scans"`
TotalRepositoriesWithFindings int `json:"total_repositories_with_findings"`
TotalSkippedRepositories int `json:"total_skipped_repositories"`
TotalSkippedAccessMismatchRepositories int `json:"total_skipped_access_mismatch_repositories"`
TotalSkippedNotFoundRepositories int `json:"total_skipped_not_found_repositories"`
TotalSkippedNoDatabaseRepositories int `json:"total_skipped_no_database_repositories"`
TotalSkippedOverLimitRepositories int `json:"total_skipped_over_limit_repositories"`
}
var results Results
for _, runId := range runIds {
for _, run := range runs {
if err != nil {
log.Fatal(err)
}
runDetails, err := getRunDetails(controller, runId)
runDetails, err := getRunDetails(controller, run.Id)
if err != nil {
log.Fatal(err)
}
@@ -713,8 +716,9 @@ Usage:
failure_reason = ""
}
results.Runs = append(results.Runs, Run{
Id: runId,
results.Runs = append(results.Runs, RunStatus{
Id: run.Id,
Query: run.Query,
Status: status,
FailureReason: failure_reason,
})
@@ -729,6 +733,7 @@ Usage:
results.ResositoriesWithFindings = append(results.ResositoriesWithFindings, RepoWithFindings{
Nwo: repoInfo["full_name"].(string),
Count: int(repo.(map[string]interface{})["result_count"].(float64)),
RunId: run.Id,
})
}
} else if repo.(map[string]interface{})["analysis_status"].(string) == "failed" {
@@ -755,9 +760,10 @@ Usage:
if err != nil {
log.Fatal(err)
}
w := &bytes.Buffer{}
jsonpretty.Format(w, bytes.NewReader(data), " ", true)
fmt.Println(w.String())
fmt.Println(string(data))
// w := &bytes.Buffer{}
// jsonpretty.Format(w, bytes.NewReader(data), " ", true)
// fmt.Println(w.String())
} else {
// Print results in a nice way
fmt.Println("Run name:", runName)
@@ -791,11 +797,10 @@ func submit(configData Config, args []string) {
nameFlag := flag.String("name", "", "Name of run")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, `
gh mrva - submit and download CodeQL queries from MRVA
fmt.Fprintf(os.Stderr, `gh mrva - Run CodeQL queries at scale using Multi-Repository Variant Analysis (MRVA)
Usage:
gh mrva submit [--codeql-dist <path to CodeQL dist>] [--controller <controller>] --lang <language> --name <run name> [--list-file <list file>] --list <list> [--query <query> | --query-suite <query suite>]
gh mrva submit [--codeql-path <path to CodeQL>] [--controller <controller>] --lang <language> --name <run name> [--list-file <list file>] --list <list> [--query <query> | --query-suite <query suite>]
`)
fmt.Fprintf(os.Stderr, "Flags:\n")
@@ -865,8 +870,8 @@ Usage:
queries = resolveQueries(codeqlPath, querySuiteFile)
}
fmt.Printf("Requesting running %d queries for %d repositories\n", len(queries), len(repositories))
var runIds []int
fmt.Printf("Submitting %d queries for %d repositories\n", len(queries), len(repositories))
var runs []Run
for _, query := range queries {
encodedBundle, err := generateQueryPack(codeqlPath, query, language)
if err != nil {
@@ -887,26 +892,26 @@ Usage:
if err != nil {
log.Fatal(err)
}
runIds = append(runIds, id)
runs = append(runs, Run{Id: id, Query: query})
}
}
fmt.Printf("Submitted runs: %v\n", runIds)
if querySuiteFile != "" {
err = saveInHistory(runName, controller, runIds, language, listFile, list, querySuiteFile, len(repositories))
err = saveInHistory(runName, controller, runs, language, listFile, list, querySuiteFile, len(repositories))
} else if queryFile != "" {
err = saveInHistory(runName, controller, runIds, language, listFile, list, queryFile, len(repositories))
err = saveInHistory(runName, controller, runs, language, listFile, list, queryFile, len(repositories))
}
if err != nil {
log.Fatal(err)
}
fmt.Println("Done!")
}
func list(args []string) {
flag := flag.NewFlagSet("mrva list", flag.ExitOnError)
jsonFlag := flag.Bool("json", false, "Output in JSON format (default: false)")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, `
gh mrva - submit and download CodeQL queries from MRVA
fmt.Fprintf(os.Stderr, `gh mrva - Run CodeQL queries at scale using Multi-Repository Variant Analysis (MRVA)
Usage:
gh mrva list [--json]
@@ -932,10 +937,10 @@ Usage:
if err != nil {
log.Fatal(err)
}
w := &bytes.Buffer{}
jsonpretty.Format(w, bytes.NewReader(data), " ", true)
fmt.Println(w.String())
fmt.Println(string(data))
// w := &bytes.Buffer{}
// jsonpretty.Format(w, bytes.NewReader(data), " ", true)
// fmt.Println(w.String())
}
} else {
for name, entry := range configData.History {
@@ -945,7 +950,10 @@ Usage:
fmt.Printf(" List file: %s\n", entry.ListFile)
fmt.Printf(" List: %s\n", entry.List)
fmt.Printf(" Repository count: %d\n", entry.RepositoryCount)
fmt.Printf(" Query(s) : %s\n", entry.Query)
for _, run := range entry.Runs {
fmt.Printf(" Run ID: %s\n", run.Id)
fmt.Printf(" Query(s) : %s\n", run.Query)
}
}
}
}
@@ -959,8 +967,7 @@ func download(args []string) {
nwoFlag := flag.String("nwo", "", "Repository to download artifacts for (optional)")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, `
gh mrva - submit and download CodeQL queries from MRVA
fmt.Fprintf(os.Stderr, `gh mrva - Run CodeQL queries at scale using Multi-Repository Variant Analysis (MRVA)
Usage:
gh mrva download --name <run name> --output-dir <output directory> [--download-dbs] [--nwo <owner/repo>]
@@ -993,22 +1000,22 @@ Usage:
}
}
controller, runIds, language, err := loadFromHistory(runName)
controller, runs, language, err := loadFromHistory(runName)
if err != nil {
log.Fatal(err)
} else if len(runIds) == 0 {
} else if len(runs) == 0 {
log.Fatal("No runs found for name " + runName)
}
var downloadTasks []DownloadTask
for _, runId := range runIds {
runDetails, err := getRunDetails(controller, runId)
for _, run := range runs {
runDetails, err := getRunDetails(controller, run.Id)
if err != nil {
log.Fatal(err)
}
if runDetails["status"] == "in_progress" {
log.Printf("Run %d is not complete yet. Please try again later.", runId)
log.Printf("Run %d is not complete yet. Please try again later.", run.Id)
return
}
for _, r := range runDetails["scanned_repositories"].([]interface{}) {
@@ -1030,7 +1037,7 @@ Usage:
_, sarifErr := os.Stat(sarifPath)
if errors.Is(bqrsErr, os.ErrNotExist) && errors.Is(sarifErr, os.ErrNotExist) {
downloadTasks = append(downloadTasks, DownloadTask{
runId: runId,
runId: run.Id,
nwo: nwo,
controller: controller,
artifact: "artifact",
@@ -1042,7 +1049,7 @@ Usage:
// check if the database already exists
if _, err := os.Stat(targetPath); errors.Is(err, os.ErrNotExist) {
downloadTasks = append(downloadTasks, DownloadTask{
runId: runId,
runId: run.Id,
nwo: nwo,
controller: controller,
artifact: "database",
@@ -1092,5 +1099,4 @@ Usage:
// drain the progress channel
<-progressDone
}