├── .github └── workflows │ └── release.yml ├── .gitignore ├── README.md ├── cmd ├── create.go ├── download.go ├── info.go ├── install.go ├── list.go └── root.go ├── go.mod ├── go.sum ├── main.go └── utils └── utils.go /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | tags: 5 | - "v*" 6 | permissions: 7 | contents: write 8 | 9 | jobs: 10 | release: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: cli/gh-extension-precompile@v1 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /gh-qldb 2 | /gh-qldb.exe 3 | gh-qldb 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ### gh-qldb 3 | 4 | Tired of having dozens of CodeQL databases scattered around your file system? Introducing QLDB, a CodeQL database manager. Download, deploy and create CodeQL databases with ease. 5 | 6 | QLDB will organize your databases in a hierarchical structure: 7 | 8 | ```bash 9 | /Users/pwntester/codeql-dbs 10 | └── github.com 11 | ├── apache 12 | │ ├── logging-log4j2 13 | │ │ ├── java─fa2f51eb.zip 14 | │ │ └── javascript─abf13fab.zip 15 | │ └── commons-text 16 | │ └── java-e2b291e9.zip 17 | └── pwntester 18 | └── sample-project 19 | └── java─9b844042.zip 20 | ``` 21 | 22 | ### Usage 23 | 24 | ```bash 25 | Usage: 26 | gh qldb [command] 27 | 28 | Available Commands: 29 | completion Generate the autocompletion script for the specified shell 30 | create Extracts a CodeQL database from a source path 31 | download Downloads a CodeQL database from GitHub Code Scanning 32 | help Help about any command 33 | install Install a local CodeQL database in the QLDB directory 34 | info Returns information about a database stored in the QLDB structure 35 | list Returns a list of CodeQL databases stored in the QLDB structure 36 | 37 | Flags: 38 | -h, --help help for gh-qldb 39 | ``` 40 | 41 | ### Examples 42 | 43 | #### Create a database 44 | 45 | ```bash 46 | gh qldb create -n foo/bar -- -s path/to/src -l java 47 | ``` 48 | 49 | #### Download a Code Scanning database 50 | 51 | ```bash 52 | gh qldb download -n apache/logging-log4j2 -l java 53 | ``` 54 | 55 | #### Install a local database in QLDB structure 56 | 57 | ```bash 58 | gh qldb install -d path/to/database -n apache/logging-log4j2 59 | ``` 60 | 61 | #### Get information about a database 62 | 63 | ```bash 64 | gh qldb info -n apache/logging-log4j2 -l java -j 65 | [ 66 | { 67 | "commitSha": "fa2f51e", 68 | "committedDate": "2023-04-06T06:25:30", 69 | "path": "/Users/pwntester/codeql-dbs/github.com/apache/logging-log4j2/java/9b84404246d516a11091e74ef4cdcf7dfcc63fa4.zip 70 | } 71 | ] 72 | ``` 73 | 74 | #### List available Databases 75 | 76 | ```bash 77 | gh qldb list 78 | /Users/pwntester/codeql-dbs/github.com/apache/logging-log4j2/java─fa2f51eb.zip 79 | /Users/pwntester/codeql-dbs/github.com/apache/logging-log4j2/javascript─abf13fab.zip 80 | /Users/pwntester/codeql-dbs/github.com/apache/commons-text/java-e2b291e9.zip 81 | /Users/pwntester/codeql-dbs/github.com/pwntester/sample-project/java─9b844042.zip 82 | ``` 83 | 84 | ### Similar projects 85 | 86 | Liked the idea? Do you want to use a similar functionality for managing your GitHub projects and clones? Try [`gh cdr`](https://github.com/pwntester/gh-cdr) 87 | -------------------------------------------------------------------------------- /cmd/create.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | var createCmd = &cobra.Command{ 14 | Use: "create", 15 | Short: "Extracts a CodeQL database from a source path", 16 | Long: `Extracts a CodeQL database from a source path. Pass the CodeQL arguments after a '--' separator. 17 | 18 | eg: gh-qldb create --nwo foo/bar -- -s /path/to/src -l javascript`, 19 | Run: func(cmd *cobra.Command, args []string) { 20 | // --nwo foo/bar -- -s /path/to/src -l javascript 21 | create(nwoFlag, args) 22 | }, 23 | } 24 | 25 | func init() { 26 | rootCmd.AddCommand(createCmd) 27 | createCmd.Flags().StringVarP(&nwoFlag, "nwo", "n", "", "The NWO of the repository to create the database for. If omitted, it will be inferred from git remotes.") 28 | } 29 | 30 | func create(nwo string, codeqlArgs []string) { 31 | fmt.Printf("Creating DB for '%s'. CodeQL args: '%v'", nwo, codeqlArgs) 32 | destPath := filepath.Join(os.TempDir(), "codeql-db") 33 | if err := os.MkdirAll(destPath, 0755); err != nil { 34 | log.Fatal(err) 35 | } 36 | args := []string{"database", "create"} 37 | args = append(args, codeqlArgs...) 38 | args = append(args, "--") 39 | args = append(args, destPath) 40 | cmd := exec.Command("codeql", args...) 41 | cmd.Env = os.Environ() 42 | _, err := cmd.CombinedOutput() 43 | if err != nil { 44 | log.Fatalln(err) 45 | } 46 | 47 | install(nwo, destPath, true) 48 | } 49 | -------------------------------------------------------------------------------- /cmd/download.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "log" 9 | "os" 10 | "path/filepath" 11 | 12 | "github.com/GitHubSecurityLab/gh-qldb/utils" 13 | "github.com/cli/go-gh" 14 | "github.com/cli/go-gh/pkg/api" 15 | "github.com/spf13/cobra" 16 | ) 17 | 18 | var downloadCmd = &cobra.Command{ 19 | Use: "download", 20 | Short: "Downloads a CodeQL database from GitHub Code Scanning", 21 | Long: `Downloads a CodeQL database from GitHub Code Scanning`, 22 | Run: func(cmd *cobra.Command, args []string) { 23 | download() 24 | }, 25 | } 26 | 27 | func init() { 28 | rootCmd.AddCommand(downloadCmd) 29 | downloadCmd.Flags().StringVarP(&nwoFlag, "nwo", "n", "", "The NWO of the repository to download the database for.") 30 | downloadCmd.Flags().StringVarP(&languageFlag, "language", "l", "", "The primary language you want the database for.") 31 | downloadCmd.MarkFlagRequired("nwo") 32 | downloadCmd.MarkFlagRequired("language") 33 | 34 | } 35 | 36 | func download() { 37 | // fetch the DB info from GitHub API 38 | fmt.Printf("Fetching DB info for '%s'\n", nwoFlag) 39 | restClient, err := gh.RESTClient(nil) 40 | response := make([]interface{}, 0) 41 | err = restClient.Get(fmt.Sprintf("repos/%s/code-scanning/codeql/databases", nwoFlag), &response) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | fmt.Print("Found DBs for the following languages: ") 46 | for i, v := range response { 47 | dbMap := v.(map[string]interface{}) 48 | language := dbMap["language"].(string) 49 | fmt.Print(language) 50 | if i < len(response)-1 { 51 | fmt.Print(", ") 52 | } 53 | } 54 | fmt.Print("\n") 55 | 56 | // download the DBs 57 | for _, v := range response { 58 | dbMap := v.(map[string]interface{}) 59 | language := dbMap["language"].(string) 60 | if languageFlag != "all" && language != languageFlag { 61 | continue 62 | } 63 | 64 | // download DB 65 | fmt.Printf("Downloading '%s' DB for '%s'\n", language, nwoFlag) 66 | opts := api.ClientOptions{ 67 | Headers: map[string]string{"Accept": "application/zip"}, 68 | } 69 | httpClient, err := gh.HTTPClient(&opts) 70 | if err != nil { 71 | log.Fatal(err) 72 | } 73 | url := fmt.Sprintf("https://api.github.com/repos/%s/code-scanning/codeql/databases/%s", nwoFlag, language) 74 | resp, err := httpClient.Get(url) 75 | if err != nil { 76 | fmt.Printf("Failure downloading the DB from `%s`: %v", url, err) 77 | continue 78 | } 79 | defer resp.Body.Close() 80 | 81 | body, err := io.ReadAll(resp.Body) 82 | if err != nil { 83 | log.Fatal(err) 84 | } 85 | 86 | // get the commit this DB was created from 87 | metadata, err := utils.ExtractDBInfo(body) 88 | if err != nil { 89 | log.Fatal(err) 90 | } 91 | metadata["provenance"] = nwoFlag 92 | commitSha := metadata["creationMetadata"].(map[string]interface{})["sha"].(string) 93 | shortCommitSha := commitSha[:8] 94 | primaryLanguage := metadata["primaryLanguage"].(string) 95 | fmt.Println() 96 | fmt.Println("Commit SHA:", commitSha) 97 | fmt.Println("Short Commit SHA:", shortCommitSha) 98 | fmt.Println("Primary language:", primaryLanguage) 99 | 100 | zipFilename := fmt.Sprintf("%s-%s.zip", primaryLanguage, shortCommitSha) 101 | jsonFilename := fmt.Sprintf("%s-%s.json", primaryLanguage, shortCommitSha) 102 | dir := utils.GetPath(nwoFlag) 103 | zipPath := filepath.Join(dir, zipFilename) 104 | jsonPath := filepath.Join(dir, jsonFilename) 105 | 106 | // create directory if not exists 107 | if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) { 108 | err := os.MkdirAll(dir, 0755) 109 | if err != nil { 110 | log.Fatal(err) 111 | } 112 | } 113 | 114 | // create DB file if doesnot exists 115 | if _, err := os.Stat(zipPath); errors.Is(err, os.ErrNotExist) { 116 | // write the DB to disk 117 | err = os.WriteFile(zipPath, body, 0755) 118 | if err != nil { 119 | log.Fatal(err) 120 | } 121 | fmt.Printf("Writing DB to %s\n", zipPath) 122 | 123 | } else { 124 | fmt.Printf("Aborting, DB %s already exists\n", zipPath) 125 | } 126 | 127 | // create Metadata file if doesnot exists 128 | if _, err := os.Stat(jsonPath); errors.Is(err, os.ErrNotExist) { 129 | // Convert the map to JSON 130 | jsonData, err := json.Marshal(metadata) 131 | if err != nil { 132 | log.Fatal(err) 133 | } 134 | // Write the JSON data to a file 135 | err = os.WriteFile(jsonPath, jsonData, 0644) 136 | if err != nil { 137 | log.Fatal(err) 138 | } 139 | } else { 140 | fmt.Printf("Aborting, DB metadata %s already exists\n", jsonPath) 141 | } 142 | } 143 | fmt.Println("Done") 144 | } 145 | -------------------------------------------------------------------------------- /cmd/info.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | 11 | "github.com/GitHubSecurityLab/gh-qldb/utils" 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | var infoCmd = &cobra.Command{ 16 | Use: "info", 17 | Short: "Returns information about a database stored in the QLDB structure", 18 | Long: `Returns information about a database stored in the QLDB structure`, 19 | Run: func(cmd *cobra.Command, args []string) { 20 | info() 21 | }, 22 | } 23 | 24 | func init() { 25 | rootCmd.AddCommand(infoCmd) 26 | infoCmd.Flags().StringVarP(&nwoFlag, "nwo", "n", "", "The NWO of the repository to get the database for.") 27 | infoCmd.Flags().StringVarP(&languageFlag, "language", "l", "", "The primary language you want the database for.") 28 | infoCmd.Flags().BoolVarP(&jsonFlag, "json", "j", false, "Use json as the output format.") 29 | infoCmd.Flags().StringVarP(&dbPathFlag, "db-path", "p", "", "Path to the database to get the info from.") 30 | infoCmd.MarkFlagsOneRequired("db-path", "nwo") 31 | infoCmd.MarkFlagsMutuallyExclusive("db-path", "nwo") 32 | } 33 | 34 | func info() { 35 | var results []map[string]string 36 | 37 | if nwoFlag != "" { 38 | results = infoFromNwo(nwoFlag) 39 | } else if dbPathFlag != "" { 40 | results = append(results, infoFromPath(dbPathFlag)) 41 | } 42 | if jsonFlag { 43 | jsonStr, err := json.MarshalIndent(results, "", " ") 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | fmt.Printf("%s", jsonStr) 48 | } else { 49 | for _, result := range results { 50 | fmt.Println(result["path"]) 51 | } 52 | } 53 | } 54 | 55 | func infoFromPath(path string) map[string]string { 56 | // get the file name part of path 57 | parts := strings.Split(path, string(os.PathSeparator)) 58 | name := parts[len(parts)-1] 59 | base := filepath.Dir(path) 60 | 61 | var dbname string 62 | if fi, err := os.Stat(path); err == nil && fi.IsDir() { 63 | dbname = path 64 | } else if filepath.Ext(name) == ".zip" { 65 | dbname = name[:len(name)-len(filepath.Ext(name))] 66 | } else { 67 | log.Fatal(fmt.Errorf("invalid database path: %s", path)) 68 | return nil 69 | } 70 | 71 | // split the name by the "-". first element is the language, second is the short commit sha 72 | nameSplit := strings.Split(dbname, "-") 73 | if len(nameSplit) != 2 { 74 | log.Fatal(fmt.Errorf("invalid database name: %s", name)) 75 | return nil 76 | } 77 | 78 | lang := nameSplit[0] 79 | shortSha := nameSplit[1] 80 | baseParts := strings.Split(base, string(os.PathSeparator)) 81 | nwo := filepath.Join(baseParts[len(baseParts)-2], baseParts[len(baseParts)-1]) 82 | commitSha, committedDate, err := utils.GetCommitInfo2(nwo, shortSha) 83 | if err != nil { 84 | log.Fatal(err) 85 | } 86 | 87 | return map[string]string{ 88 | "commitSha": commitSha, 89 | "committedDate": committedDate, 90 | "language": lang, 91 | "path": path, 92 | } 93 | } 94 | 95 | func infoFromNwo(nwo string) []map[string]string { 96 | dir := utils.GetPath(nwo) 97 | entries, err := os.ReadDir(dir) 98 | if err != nil { 99 | log.Fatal(err) 100 | } 101 | var pathList []string 102 | 103 | for _, e := range entries { 104 | entryName := e.Name() 105 | if filepath.Ext(entryName) == ".zip" || e.IsDir() { 106 | pathList = append(pathList, filepath.Join(dir, entryName)) 107 | } 108 | } 109 | 110 | var results []map[string]string 111 | for _, path := range pathList { 112 | results = append(results, infoFromPath(path)) 113 | } 114 | return results 115 | } 116 | -------------------------------------------------------------------------------- /cmd/install.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "log" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | 13 | "github.com/GitHubSecurityLab/gh-qldb/utils" 14 | "github.com/spf13/cobra" 15 | ) 16 | 17 | var installCmd = &cobra.Command{ 18 | Use: "install", 19 | Short: "Install a local CodeQL database in the QLDB directory", 20 | Long: `Install a local CodeQL database in the QLDB directory`, 21 | Run: func(cmd *cobra.Command, args []string) { 22 | install(nwoFlag, dbPathFlag, removeFlag) 23 | }, 24 | } 25 | 26 | func init() { 27 | rootCmd.AddCommand(installCmd) 28 | installCmd.Flags().StringVarP(&nwoFlag, "nwo", "n", "", "The NWO to associate the database to.") 29 | installCmd.Flags().StringVarP(&dbPathFlag, "database", "d", "", "The path to the database to install.") 30 | installCmd.Flags().BoolVarP(&removeFlag, "remove", "r", false, "Remove the database after installing it.") 31 | installCmd.MarkFlagRequired("nwo") 32 | installCmd.MarkFlagRequired("database") 33 | } 34 | 35 | func install(nwo string, dbPath string, remove bool) { 36 | fmt.Printf("Installing '%s' database for '%s'\n", dbPath, nwo) 37 | 38 | // Check if the path exists 39 | fileinfo, err := os.Stat(dbPath) 40 | var zipPath string 41 | if os.IsNotExist(err) { 42 | log.Fatal(errors.New("Database path does not exist")) 43 | } 44 | if fileinfo.IsDir() { 45 | fmt.Printf("Validating '%s' database\n", dbPath) 46 | err := utils.ValidateDB(dbPath) 47 | if err != nil { 48 | fmt.Println("Database is not valid") 49 | return 50 | } 51 | // Compress DB 52 | zipfilename := filepath.Join(os.TempDir(), "qldb.zip") 53 | fmt.Println("Compressing database") 54 | if err := utils.ZipDirectory(zipfilename, dbPath); err != nil { 55 | log.Fatal(err) 56 | } 57 | zipPath = zipfilename 58 | 59 | } else { 60 | // Check if the file is a zip 61 | if !strings.HasSuffix(dbPath, ".zip") { 62 | log.Fatal(errors.New("Database is not a zip file")) 63 | } 64 | 65 | zipPath = dbPath 66 | // Unzip to a temporary directory 67 | tmpdir, _ := os.MkdirTemp("", "qldb") 68 | 69 | _, err := utils.Unzip(dbPath, tmpdir) 70 | if err != nil { 71 | log.Fatal(err) 72 | } 73 | 74 | // Read all files in the tmpdir directory using os.ReadDir 75 | dirEntries, err := os.ReadDir(tmpdir) 76 | if err != nil { 77 | log.Fatal(err) 78 | } 79 | if len(dirEntries) == 1 { 80 | // if there is one directory in the tmpdir, use that as the tmpdir 81 | tmpdir = filepath.Join(tmpdir, dirEntries[0].Name()) 82 | } 83 | fmt.Printf("Validating '%s' database\n", tmpdir) 84 | err = utils.ValidateDB(tmpdir) 85 | if err != nil { 86 | fmt.Println("Database is not valid") 87 | } 88 | } 89 | 90 | // read bytes from dbPath 91 | zipFile, err := os.Open(zipPath) 92 | if err != nil { 93 | log.Fatal(err) 94 | } 95 | defer zipFile.Close() 96 | zipBytes, err := io.ReadAll(zipFile) 97 | if err != nil { 98 | log.Fatal(err) 99 | } 100 | 101 | metadata, err := utils.ExtractDBInfo(zipBytes) 102 | if err != nil { 103 | log.Fatal(err) 104 | } 105 | metadata["provenance"] = nwoFlag 106 | commitSha := metadata["creationMetadata"].(map[string]interface{})["sha"].(string) 107 | shortCommitSha := commitSha[:8] 108 | primaryLanguage := metadata["primaryLanguage"].(string) 109 | fmt.Println() 110 | fmt.Println("Commit SHA:", commitSha) 111 | fmt.Println("Short Commit SHA:", shortCommitSha) 112 | fmt.Println("Primary language:", primaryLanguage) 113 | 114 | zipFilename := fmt.Sprintf("%s-%s.zip", primaryLanguage, shortCommitSha) 115 | jsonFilename := fmt.Sprintf("%s-%s.json", primaryLanguage, shortCommitSha) 116 | dir := utils.GetPath(nwoFlag) 117 | 118 | // Destination path 119 | zipDestPath := filepath.Join(dir, zipFilename) 120 | jsonDestPath := filepath.Join(dir, jsonFilename) 121 | 122 | fmt.Println("Installing database to '" + zipDestPath + "'") 123 | 124 | // Check if the DB is already installed 125 | if _, err := os.Stat(zipDestPath); errors.Is(err, os.ErrNotExist) { 126 | 127 | // Create the directory if it doesn't exist 128 | err = os.MkdirAll(filepath.Dir(zipDestPath), 0755) 129 | if err != nil { 130 | log.Fatal(err) 131 | return 132 | } 133 | 134 | // Copy file from zipPath to destPath 135 | srcFile, err := os.Open(zipPath) 136 | if err != nil { 137 | log.Fatal(err) 138 | return 139 | } 140 | defer srcFile.Close() 141 | 142 | destFile, err := os.Create(zipDestPath) 143 | if err != nil { 144 | log.Fatal(err) 145 | return 146 | } 147 | defer destFile.Close() 148 | 149 | bytes, err := io.Copy(destFile, srcFile) 150 | fmt.Println(fmt.Sprintf("Copied %d bytes", bytes)) 151 | 152 | if err != nil { 153 | log.Fatal(err) 154 | } 155 | err = destFile.Sync() 156 | if err != nil { 157 | log.Fatal(err) 158 | } 159 | } else { 160 | fmt.Println("Database already installed for same commit") 161 | } 162 | 163 | if _, err := os.Stat(jsonDestPath); errors.Is(err, os.ErrNotExist) { 164 | // Convert the map to JSON 165 | jsonData, err := json.Marshal(metadata) 166 | if err != nil { 167 | log.Fatal(err) 168 | } 169 | 170 | // Write the JSON data to a file 171 | err = os.WriteFile(jsonDestPath, jsonData, 0644) 172 | if err != nil { 173 | log.Fatal(err) 174 | } 175 | } else { 176 | fmt.Println("Database metadata already exists for same commit") 177 | } 178 | 179 | // Remove DB from the current location if -r flag is set 180 | if remove { 181 | fmt.Println("Removing database from '" + dbPath + "'") 182 | if err := os.RemoveAll(dbPath); err != nil { 183 | log.Fatal(err) 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /cmd/list.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | 11 | "github.com/GitHubSecurityLab/gh-qldb/utils" 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | var listCmd = &cobra.Command{ 16 | Use: "list", 17 | Short: "Returns a list of CodeQL databases stored in the QLDB structure", 18 | Long: `Returns a list of CodeQL databases stored in the QLDB structure`, 19 | Run: func(cmd *cobra.Command, args []string) { 20 | list() 21 | }, 22 | } 23 | 24 | func init() { 25 | rootCmd.AddCommand(listCmd) 26 | listCmd.Flags().StringVarP(&nwoFlag, "nwo", "n", "", "The NWO of the repository to get the databases for.") 27 | listCmd.Flags().StringVarP(&languageFlag, "language", "l", "", "The primary language you want the databases for.") 28 | listCmd.Flags().BoolVarP(&jsonFlag, "json", "j", false, "Use json as the output format.") 29 | } 30 | 31 | func list() { 32 | var results []string 33 | basePath := utils.GetBasePath() 34 | dirEntries, err := os.ReadDir(basePath) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | for _, dirEntry := range dirEntries { 39 | if dirEntry.IsDir() { 40 | user := dirEntry.Name() 41 | userPath := filepath.Join(basePath, user) 42 | repoEntries, err := os.ReadDir(userPath) 43 | if err != nil { 44 | log.Fatal(err) 45 | } 46 | for _, repoEntry := range repoEntries { 47 | if repoEntry.IsDir() { 48 | repo := repoEntry.Name() 49 | nwoPath := filepath.Join(userPath, repo) 50 | dbEntries, err := os.ReadDir(nwoPath) 51 | if err != nil { 52 | log.Fatal(err) 53 | } 54 | for _, dbEntry := range dbEntries { 55 | if dbEntry.IsDir() || filepath.Ext(dbEntry.Name()) == ".zip" { 56 | dbPath := filepath.Join(nwoPath, dbEntry.Name()) 57 | results = append(results, dbPath) 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | 65 | // if languageFlag is set, filter the results 66 | // so that the entries in results only contains elements which filename starts with languageFlag- 67 | if languageFlag != "" { 68 | var filteredResults []string 69 | for _, result := range results { 70 | if strings.HasPrefix(filepath.Base(result), languageFlag+"-") { 71 | filteredResults = append(filteredResults, result) 72 | } 73 | } 74 | results = filteredResults 75 | } 76 | 77 | // if nwoFlag is set, filter the results so that the results only contains elements which contains nwoFlag 78 | if nwoFlag != "" { 79 | var filteredResults []string 80 | for _, result := range results { 81 | if strings.Contains(strings.ToLower(result), strings.ToLower(nwoFlag)) { 82 | filteredResults = append(filteredResults, result) 83 | } 84 | } 85 | results = filteredResults 86 | } 87 | 88 | // if jsonFlag is set, print the results as json 89 | if jsonFlag { 90 | jsonBytes, err := json.MarshalIndent(results, "", " ") 91 | if err != nil { 92 | log.Fatal(err) 93 | } 94 | fmt.Println(string(jsonBytes)) 95 | } else { 96 | for _, result := range results { 97 | fmt.Println(result) 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | var ( 10 | nwoFlag string 11 | languageFlag string 12 | removeFlag bool 13 | dbPathFlag string 14 | jsonFlag bool 15 | ) 16 | var rootCmd = &cobra.Command{ 17 | Use: "gh-qldb", 18 | Short: "A CodeQL database manager", 19 | Long: `A CodeQL database manager. Download, deploy and create CodeQL databases with ease.`, 20 | } 21 | 22 | func Execute() { 23 | err := rootCmd.Execute() 24 | if err != nil { 25 | os.Exit(1) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/GitHubSecurityLab/gh-qldb 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/cli/go-gh v1.2.1 7 | github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc 8 | github.com/spf13/cobra v1.8.0 9 | gopkg.in/yaml.v3 v3.0.1 10 | ) 11 | 12 | require ( 13 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 14 | github.com/cli/safeexec v1.0.1 // indirect 15 | github.com/cli/shurcooL-graphql v0.0.4 // indirect 16 | github.com/golang/protobuf v1.5.3 // indirect 17 | github.com/henvic/httpretty v0.1.3 // indirect 18 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 19 | github.com/kr/text v0.2.0 // indirect 20 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 21 | github.com/mattn/go-isatty v0.0.20 // indirect 22 | github.com/mattn/go-runewidth v0.0.15 // indirect 23 | github.com/muesli/termenv v0.15.2 // indirect 24 | github.com/rivo/uniseg v0.4.4 // indirect 25 | github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect 26 | github.com/spf13/pflag v1.0.5 // indirect 27 | github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e // indirect 28 | golang.org/x/net v0.20.0 // indirect 29 | golang.org/x/oauth2 v0.16.0 // indirect 30 | golang.org/x/sys v0.16.0 // indirect 31 | golang.org/x/term v0.16.0 // indirect 32 | google.golang.org/appengine v1.6.7 // indirect 33 | google.golang.org/protobuf v1.31.0 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= 2 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= 3 | github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= 4 | github.com/cli/go-gh v1.2.1 h1:xFrjejSsgPiwXFP6VYynKWwxLQcNJy3Twbu82ZDlR/o= 5 | github.com/cli/go-gh v1.2.1/go.mod h1:Jxk8X+TCO4Ui/GarwY9tByWm/8zp4jJktzVZNlTW5VM= 6 | github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= 7 | github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= 8 | github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00= 9 | github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= 10 | github.com/cli/shurcooL-graphql v0.0.2 h1:rwP5/qQQ2fM0TzkUTwtt6E2LbIYf6R+39cUXTa04NYk= 11 | github.com/cli/shurcooL-graphql v0.0.2/go.mod h1:tlrLmw/n5Q/+4qSvosT+9/W5zc8ZMjnJeYBxSdb4nWA= 12 | github.com/cli/shurcooL-graphql v0.0.4 h1:6MogPnQJLjKkaXPyGqPRXOI2qCsQdqNfUY1QSJu2GuY= 13 | github.com/cli/shurcooL-graphql v0.0.4/go.mod h1:3waN4u02FiZivIV+p1y4d0Jo1jc6BViMA73C+sZo2fk= 14 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 15 | github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 16 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 17 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 18 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 19 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 20 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 21 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 22 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 23 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 24 | github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= 25 | github.com/henvic/httpretty v0.0.6 h1:JdzGzKZBajBfnvlMALXXMVQWxWMF/ofTy8C3/OSUTxs= 26 | github.com/henvic/httpretty v0.0.6/go.mod h1:X38wLjWXHkXT7r2+uK8LjCMne9rsuNaBLJ+5cU2/Pmo= 27 | github.com/henvic/httpretty v0.1.3 h1:4A6vigjz6Q/+yAfTD4wqipCv+Px69C7Th/NhT0ApuU8= 28 | github.com/henvic/httpretty v0.1.3/go.mod h1:UUEv7c2kHZ5SPQ51uS3wBpzPDibg2U3Y+IaXyHy5GBg= 29 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 30 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 31 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 32 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 33 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 34 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 35 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 36 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 37 | github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= 38 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 39 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 40 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 41 | github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= 42 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 43 | github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= 44 | github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 45 | github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= 46 | github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc= 47 | github.com/muesli/termenv v0.12.0/go.mod h1:WCCv32tusQ/EEZ5S8oUIIrC/nIuBcxCVqlN4Xfkv+7A= 48 | github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= 49 | github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= 50 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 51 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 52 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 53 | github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= 54 | github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 55 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 56 | github.com/shurcooL/githubv4 v0.0.0-20230424031643-6cea62ecd5a9 h1:nCBaIs5/R0HFP5+aPW/SzFUF8z0oKuCXmuDmHWaxzjY= 57 | github.com/shurcooL/githubv4 v0.0.0-20230424031643-6cea62ecd5a9/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo= 58 | github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc h1:vH0NQbIDk+mJLvBliNGfcQgUmhlniWBDXC79oRxfZA0= 59 | github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8= 60 | github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 h1:B1PEwpArrNp4dkQrfxh/abbBAOZBVp0ds+fBEOUOqOc= 61 | github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg= 62 | github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0= 63 | github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE= 64 | github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= 65 | github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= 66 | github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= 67 | github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= 68 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 69 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 70 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 71 | github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e h1:BuzhfgfWQbX0dWzYzT1zsORLnHRv3bcRcsaUk0VmXA8= 72 | github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI= 73 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 74 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 75 | golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 76 | golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= 77 | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 78 | golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= 79 | golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= 80 | golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= 81 | golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= 82 | golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= 83 | golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= 84 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 85 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 86 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 87 | golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 88 | golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 89 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 90 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 91 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 92 | golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= 93 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 94 | golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= 95 | golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 96 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 97 | golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= 98 | golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= 99 | golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= 100 | golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= 101 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 102 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 103 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 104 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 105 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 106 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 107 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 108 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 109 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 110 | google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= 111 | google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= 112 | google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 113 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 114 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 115 | gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= 116 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 117 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 118 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/GitHubSecurityLab/gh-qldb/cmd" 5 | ) 6 | 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "archive/zip" 5 | "bytes" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "io" 10 | "log" 11 | "os" 12 | "os/exec" 13 | "path/filepath" 14 | "strings" 15 | 16 | "gopkg.in/yaml.v3" 17 | 18 | "github.com/cli/go-gh" 19 | graphql "github.com/shurcooL/githubv4" 20 | ) 21 | 22 | const ( 23 | ROOT = "codeql-dbs" 24 | VCS = "github.com" 25 | ) 26 | 27 | func GetBasePath() string { 28 | home := os.Getenv("HOME") 29 | return filepath.Join(home, ROOT, VCS) 30 | } 31 | 32 | func GetPath(nwo string) string { 33 | return filepath.Join(GetBasePath(), nwo) 34 | } 35 | 36 | func ValidateDB(dbPath string) error { 37 | cmd := exec.Command("codeql", "resolve", "database", dbPath) 38 | cmd.Env = os.Environ() 39 | jsonBytes, err := cmd.CombinedOutput() 40 | if err != nil { 41 | return err 42 | } 43 | var info interface{} 44 | err = json.Unmarshal(jsonBytes, &info) 45 | if err != nil { 46 | return err 47 | } 48 | return nil 49 | } 50 | 51 | func ExtractDBInfo(body []byte) (map[string]interface{}, error) { 52 | zipReader, err := zip.NewReader(bytes.NewReader(body), int64(len(body))) 53 | if err != nil { 54 | log.Fatal(err) 55 | } 56 | fmt.Print("Extracting database information ... ") 57 | for _, zf := range zipReader.File { 58 | if strings.HasSuffix(zf.Name, "codeql-database.yml") { 59 | f, err := zf.Open() 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | defer f.Close() 64 | yamlBytes, err := io.ReadAll(f) 65 | if err != nil { 66 | log.Fatal(err) 67 | } 68 | var dbData map[string]interface{} 69 | err = yaml.Unmarshal(yamlBytes, &dbData) 70 | if err != nil { 71 | log.Fatal(err) 72 | } 73 | return dbData, nil 74 | } 75 | } 76 | return nil, errors.New("codeql-database.yml not found") 77 | } 78 | 79 | // Unzip will decompress a zip archive, moving all files and folders 80 | // within the zip file (parameter 1) to an output directory (parameter 2). 81 | func Unzip(src string, dest string) ([]string, error) { 82 | 83 | var filenames []string 84 | 85 | r, err := zip.OpenReader(src) 86 | if err != nil { 87 | return filenames, err 88 | } 89 | defer r.Close() 90 | 91 | for _, f := range r.File { 92 | 93 | // Store filename/path for returning and using later on 94 | fpath := filepath.Join(dest, f.Name) 95 | 96 | // Check for ZipSlip. More Info: http://bit.ly/2MsjAWE 97 | if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) { 98 | return filenames, fmt.Errorf("%s: illegal file path", fpath) 99 | } 100 | 101 | filenames = append(filenames, fpath) 102 | 103 | if f.FileInfo().IsDir() { 104 | // Make Folder 105 | os.MkdirAll(fpath, os.ModePerm) 106 | continue 107 | } 108 | 109 | // Make File 110 | if err = os.MkdirAll(filepath.Dir(fpath), 0755); err != nil { 111 | return filenames, err 112 | } 113 | 114 | outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) 115 | if err != nil { 116 | return filenames, err 117 | } 118 | 119 | rc, err := f.Open() 120 | if err != nil { 121 | return filenames, err 122 | } 123 | 124 | _, err = io.Copy(outFile, rc) 125 | 126 | // Close the file without defer to close before next iteration of loop 127 | outFile.Close() 128 | rc.Close() 129 | 130 | if err != nil { 131 | return filenames, err 132 | } 133 | } 134 | return filenames, nil 135 | } 136 | 137 | // ZipDirectory compresses a directory into a single zip archive file. 138 | // Param 1: filename is the output zip file's name. 139 | // Param 2: directory add to the zip. 140 | func ZipDirectory(zipFileName string, directoryToZip string) error { 141 | 142 | info, err := os.Stat(directoryToZip) 143 | if err != nil { 144 | return err 145 | } 146 | if !info.IsDir() { 147 | return errors.New(directoryToZip + " is not a directory") 148 | } 149 | 150 | // Create a new zip file 151 | newZipFile, err := os.Create(zipFileName) 152 | if err != nil { 153 | return err 154 | } 155 | defer newZipFile.Close() 156 | 157 | // Create a new zip archive 158 | zipWriter := zip.NewWriter(newZipFile) 159 | defer zipWriter.Close() 160 | 161 | // Walk through the directory to zip and add each file to the archive 162 | err = filepath.Walk(directoryToZip, func(filePath string, fileInfo os.FileInfo, err error) error { 163 | if err != nil { 164 | return err 165 | } 166 | if fileInfo.IsDir() { 167 | return nil 168 | } 169 | 170 | // Create a new file header for the file 171 | fileHeader, err := zip.FileInfoHeader(fileInfo) 172 | if err != nil { 173 | return err 174 | } 175 | 176 | // Set the name of the file header to the relative path of the file 177 | // get parent directory name 178 | relPath, err := filepath.Rel(directoryToZip, filePath) 179 | if err != nil { 180 | return err 181 | } 182 | fileHeader.Name = filepath.Join("codeql-db", relPath) 183 | 184 | // Add the file header to the archive 185 | writer, err := zipWriter.CreateHeader(fileHeader) 186 | if err != nil { 187 | return err 188 | } 189 | 190 | // Open the file for reading 191 | fileToZip, err := os.Open(filePath) 192 | if err != nil { 193 | return err 194 | } 195 | defer fileToZip.Close() 196 | 197 | // Copy the file to the archive 198 | _, err = io.Copy(writer, fileToZip) 199 | if err != nil { 200 | return err 201 | } 202 | 203 | return nil 204 | }) 205 | 206 | if err != nil { 207 | return err 208 | } 209 | 210 | fmt.Printf("Successfully created zip file %s\n", zipFileName) 211 | 212 | return nil 213 | } 214 | 215 | func GetCommitInfo(nwo string, commitSha string) (string, string, error) { 216 | 217 | graphqlClient, err := gh.GQLClient(nil) 218 | if err != nil { 219 | return "", "", err 220 | } 221 | var query struct { 222 | Repository struct { 223 | Object struct { 224 | Commit struct { 225 | AbbreviatedOid graphql.String 226 | CommittedDate graphql.DateTime 227 | } `graphql:"... on Commit"` 228 | } `graphql:"object(oid: $sha)"` 229 | } `graphql:"repository(owner: $owner, name: $name)"` 230 | } 231 | 232 | variables := map[string]interface{}{ 233 | "owner": graphql.String(strings.Split(nwo, "/")[0]), 234 | "name": graphql.String(strings.Split(nwo, "/")[1]), 235 | "sha": graphql.GitObjectID(commitSha), 236 | } 237 | err = graphqlClient.Query("CommitInfo", &query, variables) 238 | if err != nil { 239 | return "", "", err 240 | } 241 | return string(query.Repository.Object.Commit.AbbreviatedOid), query.Repository.Object.Commit.CommittedDate.Format("2006-01-02T15:04:05"), nil 242 | } 243 | 244 | func GetCommitInfo2(nwo string, commitSha string) (string, string, error) { 245 | restClient, err := gh.RESTClient(nil) 246 | response := make(map[string]interface{}) 247 | err = restClient.Get(fmt.Sprintf("repos/%s/commits/%s", nwo, commitSha), &response) 248 | if err != nil { 249 | log.Fatal(err) 250 | } 251 | commitMap := response["commit"].(map[string]interface{}) 252 | committerMap := commitMap["committer"].(map[string]interface{}) 253 | dateString := committerMap["date"].(string) 254 | return commitSha, dateString, nil 255 | } 256 | --------------------------------------------------------------------------------