Skip to content

Commit 4e6f037

Browse files
author
Alvaro Muñoz
committed
initial commit
0 parents  commit 4e6f037

File tree

11 files changed

+757
-0
lines changed

11 files changed

+757
-0
lines changed

.github/workflows/release.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/gh-qldb
2+
/gh-qldb.exe
3+
gh-qldb

cmd/create.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package cmd
2+
3+
import (
4+
"log"
5+
"fmt"
6+
"path/filepath"
7+
"os/exec"
8+
"os"
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+
}

cmd/download.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package cmd
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"io/ioutil"
7+
"log"
8+
"os"
9+
"path/filepath"
10+
11+
"github.com/cli/go-gh"
12+
"github.com/cli/go-gh/pkg/api"
13+
"github.com/spf13/cobra"
14+
"github.com/GitHubSecurityLab/gh-qldb/utils"
15+
)
16+
17+
var downloadCmd = &cobra.Command{
18+
Use: "download",
19+
Short: "Downloads a CodeQL database from GitHub Code Scanning",
20+
Long: `Downloads a CodeQL database from GitHub Code Scanning`,
21+
Run: func(cmd *cobra.Command, args []string) {
22+
download()
23+
},
24+
}
25+
26+
func init() {
27+
rootCmd.AddCommand(downloadCmd)
28+
downloadCmd.Flags().StringVarP(&nwoFlag, "nwo", "n", "", "The NWO of the repository to download the database for.")
29+
downloadCmd.Flags().StringVarP(&languageFlag, "language", "l", "", "The primary language you want the database for.")
30+
downloadCmd.MarkFlagRequired("nwo")
31+
downloadCmd.MarkFlagRequired("language")
32+
33+
}
34+
35+
func download() {
36+
// fetch the DB info from GitHub API
37+
fmt.Printf("Fetching DB info for '%s'\n", nwoFlag)
38+
restClient, err := gh.RESTClient(nil)
39+
response := make([]interface{}, 0)
40+
err = restClient.Get(fmt.Sprintf("repos/%s/code-scanning/codeql/databases", nwoFlag), &response)
41+
if err != nil {
42+
log.Fatal(err)
43+
}
44+
fmt.Print("Found DBs for the following languages: ")
45+
for i, v := range response {
46+
dbMap := v.(map[string]interface{})
47+
language := dbMap["language"].(string)
48+
fmt.Print(language)
49+
if i < len(response)-1 {
50+
fmt.Print(", ")
51+
}
52+
}
53+
fmt.Print("\n")
54+
55+
// download the DBs
56+
for _, v := range response {
57+
dbMap := v.(map[string]interface{})
58+
language := dbMap["language"].(string)
59+
if languageFlag != "all" && language != languageFlag {
60+
continue
61+
}
62+
63+
// download DB
64+
fmt.Printf("Downloading '%s' DB for '%s'\n", language, nwoFlag)
65+
opts := api.ClientOptions{
66+
Headers: map[string]string{"Accept": "application/zip"},
67+
}
68+
httpClient, err := gh.HTTPClient(&opts)
69+
if err != nil {
70+
log.Fatal(err)
71+
}
72+
url := fmt.Sprintf("https://api.github.com/repos/%s/code-scanning/codeql/databases/%s", nwoFlag, language)
73+
resp, err := httpClient.Get(url)
74+
if err != nil {
75+
fmt.Printf("Failure downloading the DB from `%s`: %v", url, err)
76+
continue
77+
}
78+
defer resp.Body.Close()
79+
80+
body, err := ioutil.ReadAll(resp.Body)
81+
if err != nil {
82+
log.Fatal(err)
83+
}
84+
85+
// get the commit this DB was created from
86+
commitSha, primaryLanguage, err := utils.ExtractDBInfo(body)
87+
if err != nil {
88+
log.Fatal(err)
89+
}
90+
91+
filename := fmt.Sprintf("%s.zip", commitSha)
92+
dir := filepath.Join(utils.GetPath(nwoFlag), primaryLanguage)
93+
path := filepath.Join(dir, filename)
94+
95+
// create directory if not exists
96+
if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) {
97+
err := os.MkdirAll(dir, 0755)
98+
if err != nil {
99+
log.Fatal(err)
100+
}
101+
}
102+
103+
// create file if not exists
104+
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
105+
// write the DB to disk
106+
err = ioutil.WriteFile(path, body, 0755)
107+
if err != nil {
108+
log.Fatal(err)
109+
}
110+
fmt.Printf("Writing DB to %s\n", path)
111+
112+
} else {
113+
fmt.Printf("Aborting, DB %s already exists\n", path)
114+
}
115+
116+
}
117+
fmt.Println("Done")
118+
}
119+

cmd/install.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package cmd
2+
3+
import (
4+
"io"
5+
"errors"
6+
"fmt"
7+
"io/ioutil"
8+
"log"
9+
"os"
10+
"path/filepath"
11+
"strings"
12+
13+
"github.com/spf13/cobra"
14+
"github.com/GitHubSecurityLab/gh-qldb/utils"
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' DB 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("DB path does not exist"))
43+
}
44+
if fileinfo.IsDir() {
45+
err := utils.ValidateDB(dbPath)
46+
if err != nil {
47+
fmt.Println("DB is not valid")
48+
}
49+
// Compress DB
50+
zipfilename := filepath.Join(os.TempDir(), "qldb.zip")
51+
fmt.Println("Zipping DB to", zipfilename)
52+
if err := utils.ZipDirectory(zipfilename, dbPath); err != nil {
53+
log.Fatal(err)
54+
}
55+
zipPath = zipfilename
56+
57+
} else {
58+
// Check if the file is a zip
59+
if !strings.HasSuffix(dbPath, ".zip") {
60+
log.Fatal(errors.New("DB path is not a zip file"))
61+
}
62+
63+
zipPath = dbPath
64+
// Unzip to temporary directory
65+
tmpdir, _ := ioutil.TempDir("", "qldb")
66+
_, err := utils.Unzip(dbPath, tmpdir)
67+
if err != nil {
68+
log.Fatal(err)
69+
}
70+
files, err := ioutil.ReadDir(tmpdir)
71+
if err != nil {
72+
log.Fatal(err)
73+
}
74+
if len(files) == 1 {
75+
tmpdir = filepath.Join(tmpdir, files[0].Name())
76+
}
77+
err = utils.ValidateDB(tmpdir)
78+
if err != nil {
79+
fmt.Println("DB is not valid")
80+
}
81+
}
82+
83+
// read bytes from dbPath
84+
zipFile, err := os.Open(zipPath)
85+
if err != nil {
86+
log.Fatal(err)
87+
}
88+
defer zipFile.Close()
89+
zipBytes, err := ioutil.ReadAll(zipFile)
90+
if err != nil {
91+
log.Fatal(err)
92+
}
93+
commitSha, primaryLanguage, err := utils.ExtractDBInfo(zipBytes)
94+
95+
// Destination path
96+
dir := filepath.Join(utils.GetPath(nwo), primaryLanguage)
97+
filename := fmt.Sprintf("%s.zip", commitSha)
98+
path := filepath.Join(dir, filename)
99+
fmt.Println("Installing DB to", path)
100+
101+
// Check if the DB is already installed
102+
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
103+
// Copy DB to the right place
104+
srcFile, err := os.Open(zipPath)
105+
if err != nil {
106+
log.Fatal(err)
107+
}
108+
defer srcFile.Close()
109+
err = os.MkdirAll(filepath.Dir(path), 0755)
110+
if err != nil {
111+
log.Fatal(err)
112+
}
113+
destFile, err := os.Create(path)
114+
if err != nil {
115+
log.Fatal(err)
116+
}
117+
defer destFile.Close()
118+
fmt.Println("Copying DB to", path)
119+
_, err = io.Copy(srcFile, destFile) // check first var for number of bytes copied
120+
if err != nil {
121+
log.Fatal(err)
122+
}
123+
err = destFile.Sync()
124+
if err != nil {
125+
log.Fatal(err)
126+
}
127+
}
128+
// Remove DB from the current location if -r flag is set
129+
if remove {
130+
fmt.Println("Removing DB from", dbPath)
131+
os.Remove(dbPath)
132+
}
133+
}

cmd/path.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package cmd
2+
3+
import (
4+
"github.com/GitHubSecurityLab/gh-qldb/utils"
5+
"github.com/spf13/cobra"
6+
"path/filepath"
7+
"log"
8+
"fmt"
9+
"io/ioutil"
10+
"encoding/json"
11+
)
12+
var pathCmd = &cobra.Command{
13+
Use: "path",
14+
Short: "Returns the path to a database stored in the QLDB structure",
15+
Long: `Returns the path to a database stored in the QLDB structure`,
16+
Run: func(cmd *cobra.Command, args []string) {
17+
path(nwoFlag, languageFlag)
18+
},
19+
}
20+
21+
func init() {
22+
rootCmd.AddCommand(pathCmd)
23+
pathCmd.Flags().StringVarP(&nwoFlag, "nwo", "n", "", "The NWO of the repository to get the database for.")
24+
pathCmd.Flags().StringVarP(&languageFlag, "language", "l", "", "The primary language you want the database for.")
25+
pathCmd.Flags().BoolVarP(&jsonFlag, "json", "j", false, "Use json as the output format.")
26+
pathCmd.MarkFlagRequired("nwo")
27+
pathCmd.MarkFlagRequired("language")
28+
}
29+
30+
func path(nwo string, language string) {
31+
dir := filepath.Join(utils.GetPath(nwo), language)
32+
files, err := ioutil.ReadDir(dir)
33+
if err != nil {
34+
log.Fatal(err)
35+
}
36+
var pathList []map[string]string
37+
for _, f := range files {
38+
filename := f.Name()
39+
sha := filename[:len(filename)-len(filepath.Ext(filename))]
40+
commitSha, committedDate, err := utils.GetCommitInfo(nwo, sha)
41+
if err != nil {
42+
log.Fatal(err)
43+
}
44+
pathList = append(pathList, map[string]string{
45+
"commitSha": commitSha,
46+
"committedDate": committedDate,
47+
"path": filepath.Join(dir, filename),
48+
})
49+
}
50+
if jsonFlag {
51+
jsonStr, err := json.MarshalIndent(pathList, "", " ")
52+
if err != nil {
53+
log.Fatal(err)
54+
}
55+
fmt.Printf("%s", jsonStr)
56+
} else {
57+
for _, path := range pathList {
58+
fmt.Printf("%s (%s)", path["path"], path["committedDate"])
59+
}
60+
}
61+
}
62+

0 commit comments

Comments
 (0)