Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package gokart for openSUSE:Factory checked in at 2021-10-11 15:31:27 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/gokart (Old) and /work/SRC/openSUSE:Factory/.gokart.new.2443 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "gokart" Mon Oct 11 15:31:27 2021 rev:3 rq:924309 version:0.3.0 Changes: -------- --- /work/SRC/openSUSE:Factory/gokart/gokart.changes 2021-09-14 21:14:25.780407387 +0200 +++ /work/SRC/openSUSE:Factory/.gokart.new.2443/gokart.changes 2021-10-11 15:32:23.070918773 +0200 @@ -1,0 +2,20 @@ +Fri Oct 08 14:05:36 UTC 2021 - alexandre.vice...@suse.com + +- Update to version 0.3.0: + * Enhancements + * Output findings to file CLI flag + * Verbose output source parent function fix + * Add exit code on findings flag + * Update remote scanning functionality to include private repos + * Add basic dockerfile and README + * CWE mappings for findings + * Output total findings information + * Output findings as json + * Highlight relevant finding lines + * Bug Fixes + * Channel incorrectly identified as source of untrusted input + * Verbose trace has incorrect parent signatures + * panic: err: go command required + * panic: index out of range + +------------------------------------------------------------------- @@ -5,9 +25,3 @@ - * Add new flags usage to Getting Started (#31) - * Adding -r flag to automatically clone and test remote go modules, along with functionality to test the Scan cmd (#20) - * Making a minor change to error messages when packages aren't found in the directory (#28) - * Feature enhancement implementation for Issue #3 (Findings Output to File Flag) (#24) - * changed panic to log.Fatal (#22) - * removed non-user-controllable input sources (#23) - * typo fixes (#14) - * Improved config mgmt and fixed out of bounds panic (#5) - * add link to Praetorian blog post in README + * Added support for writing findings output to a file to ease CI/CD integration via the -o flag. + * Added remote fetch + scan functionality via the -r flag. + * Updated README to cover new functionality. Old: ---- gokart-0.2.0.tar.gz New: ---- gokart-0.3.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ gokart.spec ++++++ --- /var/tmp/diff_new_pack.i91Tll/_old 2021-10-11 15:32:23.758919876 +0200 +++ /var/tmp/diff_new_pack.i91Tll/_new 2021-10-11 15:32:23.762919883 +0200 @@ -17,7 +17,7 @@ Name: gokart -Version: 0.2.0 +Version: 0.3.0 Release: 0 Summary: Static analysis tool for securing Go code License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.i91Tll/_old 2021-10-11 15:32:23.786919921 +0200 +++ /var/tmp/diff_new_pack.i91Tll/_new 2021-10-11 15:32:23.786919921 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/praetorian-inc/gokart.git</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.2.0</param> + <param name="revision">v0.3.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="changesgenerate">enable</param> <param name="versionrewrite-pattern">v(.*)</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.i91Tll/_old 2021-10-11 15:32:23.802919947 +0200 +++ /var/tmp/diff_new_pack.i91Tll/_new 2021-10-11 15:32:23.802919947 +0200 @@ -1,4 +1,4 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/praetorian-inc/gokart.git</param> - <param name="changesrevision">fecb3b0f40927e14053bbb6dfbce3160e8b388bb</param></service></servicedata> \ No newline at end of file + <param name="changesrevision">bb678c03b385071f6a160bd8baed0dacf399165e</param></service></servicedata> \ No newline at end of file ++++++ gokart-0.2.0.tar.gz -> gokart-0.3.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/Dockerfile new/gokart-0.3.0/Dockerfile --- old/gokart-0.2.0/Dockerfile 1970-01-01 01:00:00.000000000 +0100 +++ new/gokart-0.3.0/Dockerfile 2021-09-20 23:52:51.000000000 +0200 @@ -0,0 +1,8 @@ +# syntax=docker/dockerfile:1 + +FROM golang:1.16-alpine +WORKDIR /app +COPY . /app/ +RUN go build -o /gokart + +ENTRYPOINT [ "/gokart" ] \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/README.md new/gokart-0.3.0/README.md --- old/gokart-0.2.0/README.md 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/README.md 2021-09-20 23:52:51.000000000 +0200 @@ -83,6 +83,25 @@ $ mv ./gokart /usr/local/bin ``` +### Docker Support +Build the docker image +```shell +docker build -t gokart . +``` + +Running the container with a local scan (the local scan directory needs to be mounted to the container image) +```shell +docker run -v /path/to/scan-dir:/scan-dir gokart scan /scan-dir +``` + +Running the container with a remote scan (when specifying a private key for auth, that will also need to be mounted to the container) +```shell +docker run gokart scan -r https://github.com/praetorian-inc/gokart + +# specifying a private key for private repository ssh authentication +docker run -v /path/to/key-dir/:/key-dir gokart scan -r g...@github.com:praetorian-inc/gokart.git -k /key-dir/ssh_key +``` + ## Usage ### Run GoKart on a Go module in the current directory @@ -141,15 +160,24 @@ # Output scarif results to file gokart scan go-test-bench/ -o gokart-go-test-bench.txt -s -# Scan remote repository (private repos require proper authentication) +# Scan remote public repository # Repository will be cloned locally, scanned and deleted afterwards -gokart scan -r github.com/ShiftLeftSecurity/shiftleft-go-demo -v +gokart scan -r https://github.com/ShiftLeftSecurity/shiftleft-go-demo -v + +# Specify the remote branch to scan +gokart scan -r https://github.com/ShiftLeftSecurity/shiftleft-go-demo -b actions_fix + +# Scan remote private repository via ssh +gokart scan -r g...@github.com:Contrast-Security-OSS/go-test-bench.git + +# Scan remote private repository and optionally specify a key for ssh authentication +gokart scan -r g...@github.com:Contrast-Security-OSS/go-test-bench.git -k /home/gokart/.ssh/github_rsa_key # Use remote scan and output flags together for seamless security reviews -gokart scan -r github.com/ShiftLeftSecurity/shiftleft-go-demo -o gokart-shiftleft-go-demo.txt -v +gokart scan -r https://github.com/ShiftLeftSecurity/shiftleft-go-demo -o gokart-shiftleft-go-demo.txt -v # Use remote scan, output and sarif flags for frictionless integration into CI/CD -gokart scan -r github.com/ShiftLeftSecurity/shiftleft-go-demo -o gokart-shiftleft-go-demo.txt -s +gokart scan -r https://github.com/ShiftLeftSecurity/shiftleft-go-demo -o gokart-shiftleft-go-demo.txt -s ``` To test out the extensibility of GoKart, you can modify the configuration file that GoKart uses to diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/analyzers/cmdi.go new/gokart-0.3.0/analyzers/cmdi.go --- old/gokart-0.2.0/analyzers/cmdi.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/analyzers/cmdi.go 2021-09-20 23:52:51.000000000 +0200 @@ -75,7 +75,7 @@ message := "Danger: possible command injection detected" targetFunc := util.GenerateTaintedCode(pass, vulnFunc.Fn, vulnFunc.Instr.Pos()) taintSource := taintAnalyzer.TaintSource - finding := util.MakeFinding(message, targetFunc, taintSource, "Command Injection") + finding := util.MakeFinding(message, targetFunc, taintSource, "CWE-78: OS Command Injection") results = append(results, finding) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/analyzers/cmdi_test.go new/gokart-0.3.0/analyzers/cmdi_test.go --- old/gokart-0.2.0/analyzers/cmdi_test.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/analyzers/cmdi_test.go 2021-09-20 23:52:51.000000000 +0200 @@ -39,7 +39,7 @@ } for i := 0; i < len(testFiles); i++ { t.Run(testFiles[i], func(t *testing.T) { - testutil.RunTest(testFiles[i], testResults[i], "Command Injection", CommandInjectionAnalyzer, t) + testutil.RunTest(testFiles[i], testResults[i], "CWE-78: OS Command Injection", CommandInjectionAnalyzer, t) }) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/analyzers/rsa.go new/gokart-0.3.0/analyzers/rsa.go --- old/gokart-0.2.0/analyzers/rsa.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/analyzers/rsa.go 2021-09-20 23:52:51.000000000 +0200 @@ -173,9 +173,9 @@ // Check if argument of vulnerable function has keylen that is less than RECOMMENDED_KEYLEN if keylen_check(pass, vulnFunc.Instr.Call.Args[1], call_graph) { - message := fmt.Sprintf("Danger: key length is too short, recommend %d", RECOMMENDED_KEYLEN) + message := fmt.Sprintf("Danger: RSA key length is too short, recommend %d", RECOMMENDED_KEYLEN) targetFunc := util.GenerateTaintedCode(pass, vulnFunc.Fn, vulnFunc.Instr.Pos()) - results = append(results, util.MakeFinding(message, targetFunc, nil, "RSA Key Length")) + results = append(results, util.MakeFinding(message, targetFunc, nil, "CWE-326: Inadequate Encryption Strength")) } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/analyzers/rsa_test.go new/gokart-0.3.0/analyzers/rsa_test.go --- old/gokart-0.2.0/analyzers/rsa_test.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/analyzers/rsa_test.go 2021-09-20 23:52:51.000000000 +0200 @@ -51,7 +51,7 @@ } for i := 0; i < len(testFiles); i++ { t.Run(testFiles[i], func(t *testing.T) { - testutil.RunTest(testFiles[i], testResults[i], "RSA Key Length", RsaKeylenAnalyzer, t) + testutil.RunTest(testFiles[i], testResults[i], "CWE-326: Inadequate Encryption Strength", RsaKeylenAnalyzer, t) }) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/analyzers/scan.go new/gokart-0.3.0/analyzers/scan.go --- old/gokart-0.2.0/analyzers/scan.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/analyzers/scan.go 2021-09-20 23:52:51.000000000 +0200 @@ -19,6 +19,8 @@ package analyzers import ( + "encoding/json" + "errors" "fmt" "log" "os" @@ -59,6 +61,7 @@ func OutputResults(results []util.Finding, success bool) error { var stdOutPipe, outputFile *os.File + var outputColor = true if util.Config.OutputPath != "" { stdOutPipe = os.Stdout // keep backup of the real stdout @@ -68,10 +71,27 @@ return err } os.Stdout = outputFile + outputColor = false + } + + if util.Config.OutputJSON && success { + res, err := json.Marshal(results) + if err != nil { + return err + } + fmt.Println(string(res)) + } + + if util.Config.OutputJSON && success { + res, err := json.Marshal(results) + if err != nil { + return err + } + fmt.Println(string(res)) } for _, finding := range results { - util.OutputFinding(finding) + util.OutputFinding(finding, outputColor) } // if packages were able to be scanned, print the correct output message @@ -82,6 +102,8 @@ // if output was redirected for findings, change it back to the original stdout if util.Config.OutputPath != "" { + // also generate the count of findings identified to the output file + util.OutputFindingMetadata(results, outputColor) outputFile.Close() os.Stdout = stdOutPipe // restoring the real stdout } @@ -89,7 +111,7 @@ return nil } -func Scan(args []string) { +func Scan(args []string) ([]util.Finding, error) { //Get the current dir so we can reset it later. current_dir, err := os.Getwd() if err != nil { @@ -98,6 +120,8 @@ if util.Config.OutputSarif { util.InitSarifReporting() + } else if util.Config.OutputJSON { + // don't print out anything } else { fmt.Printf("\nRevving engines VRMMM VRMMM\n3...2...1...Go!\n") } @@ -185,9 +209,18 @@ log.Fatalf("Error opening output file: %v", err) } - if !util.Config.OutputSarif && success { + // Don't print out messages if JSON or SARIF output + if !(util.Config.OutputSarif || util.Config.OutputJSON) && success { fmt.Println("\nRace Complete! Analysis took", scan_time, "and", util.FilesFound, "Go files were scanned (including imported packages)") fmt.Printf("GoKart found %d potentially vulnerable functions\n", len(filteredResults)) + // display information about all findings + util.OutputFindingMetadata(filteredResults, true) } os.Chdir(current_dir) + + if !success { + return nil, errors.New("gokart could not find any packages to scan") + } + + return filteredResults, nil } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/analyzers/sqli.go new/gokart-0.3.0/analyzers/sqli.go --- old/gokart-0.2.0/analyzers/sqli.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/analyzers/sqli.go 2021-09-20 23:52:51.000000000 +0200 @@ -80,7 +80,7 @@ message := "Danger: possible SQL injection detected" targetFunc := util.GenerateTaintedCode(pass, vulnFunc.Fn, vulnFunc.Instr.Pos()) taintSource := taint_analyzer.TaintSource - results = append(results, util.MakeFinding(message, targetFunc, taintSource, "SQL Injection")) + results = append(results, util.MakeFinding(message, targetFunc, taintSource, "CWE-89: SQL Injection")) } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/analyzers/sqli_test.go new/gokart-0.3.0/analyzers/sqli_test.go --- old/gokart-0.2.0/analyzers/sqli_test.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/analyzers/sqli_test.go 2021-09-20 23:52:51.000000000 +0200 @@ -39,7 +39,7 @@ } for i := 0; i < len(testFiles); i++ { t.Run(testFiles[i], func(t *testing.T) { - testutil.RunTest(testFiles[i], testResults[i], "SQL Injection", SQLInjectionAnalyzer, t) + testutil.RunTest(testFiles[i], testResults[i], "CWE-89: SQL Injection", SQLInjectionAnalyzer, t) }) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/analyzers/ssrf.go new/gokart-0.3.0/analyzers/ssrf.go --- old/gokart-0.2.0/analyzers/ssrf.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/analyzers/ssrf.go 2021-09-20 23:52:51.000000000 +0200 @@ -158,7 +158,7 @@ message := "Danger: possible SSRF detected" targetFunc := util.GenerateTaintedCode(pass, vulnFunc.Fn, vulnFunc.Instr.Pos()) taintSource := taintAnalyzer.TaintSource - results = append(results, util.MakeFinding(message, targetFunc, taintSource, "SSRF")) + results = append(results, util.MakeFinding(message, targetFunc, taintSource, "CWE-918: Server-Side Request Forgery")) } } @@ -169,7 +169,7 @@ message := "Danger: possible SSRF detected" targetFunc := util.GenerateTaintedCode(pass, vulnFunc.Fn, vulnFunc.Instr.Pos()) taintSource := taintAnalyzer.TaintSource - results = append(results, util.MakeFinding(message, targetFunc, taintSource, "SSRF")) + results = append(results, util.MakeFinding(message, targetFunc, taintSource, "CWE-918: Server-Side Request Forgery")) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/analyzers/ssrf_test.go new/gokart-0.3.0/analyzers/ssrf_test.go --- old/gokart-0.2.0/analyzers/ssrf_test.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/analyzers/ssrf_test.go 2021-09-20 23:52:51.000000000 +0200 @@ -47,7 +47,7 @@ } for i := 0; i < len(testFiles); i++ { t.Run(testFiles[i], func(t *testing.T) { - testutil.RunTest(testFiles[i], testResults[i], "SSRF", SSRFAnalyzer, t) + testutil.RunTest(testFiles[i], testResults[i], "CWE-918: Server-Side Request Forgery", SSRFAnalyzer, t) }) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/analyzers/traversal.go new/gokart-0.3.0/analyzers/traversal.go --- old/gokart-0.2.0/analyzers/traversal.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/analyzers/traversal.go 2021-09-20 23:52:51.000000000 +0200 @@ -75,7 +75,7 @@ targetFunc := util.GenerateTaintedCode(pass, vulnFunc.Fn, vulnFunc.Instr.Pos()) taintSource := taintAnalyzer.TaintSource - results = append(results, util.MakeFinding(message, targetFunc, taintSource, "Path Traversal")) + results = append(results, util.MakeFinding(message, targetFunc, taintSource, "CWE-22: Path Traversal")) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/analyzers/traversal_test.go new/gokart-0.3.0/analyzers/traversal_test.go --- old/gokart-0.2.0/analyzers/traversal_test.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/analyzers/traversal_test.go 2021-09-20 23:52:51.000000000 +0200 @@ -51,7 +51,7 @@ } for i := 0; i < len(testFiles); i++ { t.Run(testFiles[i], func(t *testing.T) { - testutil.RunTest(testFiles[i], testResults[i], "Path Traversal", PathTraversalAnalyzer, t) + testutil.RunTest(testFiles[i], testResults[i], "CWE-22: Path Traversal", PathTraversalAnalyzer, t) }) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/cmd/scan.go new/gokart-0.3.0/cmd/scan.go --- old/gokart-0.2.0/cmd/scan.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/cmd/scan.go 2021-09-20 23:52:51.000000000 +0200 @@ -18,26 +18,34 @@ package cmd import ( + "io/ioutil" "log" + "os" "github.com/praetorian-inc/gokart/analyzers" "github.com/praetorian-inc/gokart/util" "github.com/spf13/cobra" - ) var yml string -var goModName string +var exitCode bool +var remoteModule string var outputPath string +var remoteBranch string +var keyFile string func init() { goKartCmd.AddCommand(scanCmd) scanCmd.Flags().BoolP("sarif", "s", false, "outputs findings in SARIF form") + scanCmd.Flags().BoolP("json", "j", false, "outputs findings in JSON") scanCmd.Flags().BoolP("globalsTainted", "g", false, "marks global variables as dangerous") scanCmd.Flags().BoolP("verbose", "v", false, "outputs full trace of taint analysis") scanCmd.Flags().BoolP("debug", "d", false, "outputs debug logs") - scanCmd.Flags().StringVarP(&goModName, "remoteModule", "r", "", "Remote gomodule to scan") - scanCmd.Flags().StringVarP(&yml, "input", "i", "", "input path to custom yml file") + scanCmd.Flags().BoolP("exitCode", "x", false, "return non-nil exit code on potential vulnerabilities or scanner failure") + scanCmd.Flags().StringVarP(&remoteModule, "remoteModule", "r", "", "Remote gomodule to scan") + scanCmd.Flags().StringVarP(&remoteBranch, "remoteBranch", "b", "", "Branch of remote module to scan") + scanCmd.Flags().StringVarP(&keyFile, "keyFile", "k", "", "SSH Keyfile to use for ssh authentication for remote git repository scanning") + scanCmd.Flags().StringVarP(&yml, "input", "i", "", "input path to custom yml file") scanCmd.Flags().StringVarP(&outputPath, "output", "o", "", "file path to write findings output instead of stdout") goKartCmd.MarkFlagRequired("scan") } @@ -49,31 +57,41 @@ Scans a Go module directory. To scan the current directory recursively, use gokart scan. To scan a specific directory, use gokart scan <directory>.`, Run: func(cmd *cobra.Command, args []string) { sarif, _ := cmd.Flags().GetBool("sarif") + json, _ := cmd.Flags().GetBool("json") globals, _ := cmd.Flags().GetBool("globalsTainted") verbose, _ := cmd.Flags().GetBool("verbose") debug, _ := cmd.Flags().GetBool("debug") - util.InitConfig(globals, sarif, verbose, debug, outputPath, yml) - - // If gomodname flag is set to a non-empty value then clone the repo and scan it - if len(goModName) != 0 { - modDirName, err := util.ParseModuleName(goModName) + exitCode, _ := cmd.Flags().GetBool("exitCode") + util.InitConfig(globals, sarif, json, verbose, debug, outputPath, yml, exitCode) + + // If remoteModule was set, clone the remote repository and scan it + if len(remoteModule) != 0 { + moduleTempDir, err := ioutil.TempDir(".", "gokart") if err != nil { - log.Fatal(err) + log.Fatal("Error creating temporary directory: ", err.Error()) } - err = util.CloneModule(modDirName, "https://"+goModName) + defer util.CleanupModule(moduleTempDir) + + // Clone the module, if the output format is JSON or SARIF don't print any progress to stdout + err = util.CloneModule(moduleTempDir, remoteModule, remoteBranch, keyFile, json || sarif) + if err != nil { - log.Fatal("GoKart was unable to get the new racetrack. Ensure track repository is open to the public or that your access tokens are configured correctly for Private ones.") + util.CleanupModule(moduleTempDir) + log.Fatal("Error cloning remote repository: ", err.Error()) } - defer util.CleanupModule(modDirName) // If passing in a module - the other arguments are wiped out! - args = append([]string{}, modDirName+"/...") + args = append([]string{}, moduleTempDir+"/...") } // recursively scan the current directory if no arguments are passed in if len(args) == 0 { args = append(args, "./...") } - - analyzers.Scan(args) + + results, err := analyzers.Scan(args) + // If we have set the flag to return non-zero exit code for when results are found or the scanner fails, return 1 + if exitCode && (err != nil || len(results) > 0) { + os.Exit(1) + } }, } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/cmd/scan_test.go new/gokart-0.3.0/cmd/scan_test.go --- old/gokart-0.2.0/cmd/scan_test.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/cmd/scan_test.go 2021-09-20 23:52:51.000000000 +0200 @@ -1,77 +1,76 @@ -package cmd - -import ( - "testing" - "strings" - "fmt" - "os" - "io/ioutil" - - "github.com/praetorian-inc/gokart/util" - "github.com/spf13/cobra" -) - -func TestScanCommand(t *testing.T) { - // Tests the Scan command. - cur_dir, err := os.Getwd() - if err != nil { - fmt.Println(err) - os.Exit(1) - } - fmt.Printf("Current dir is: %s", cur_dir) - var tests = []struct { - args []string - expected_lastline string - moduledir string - }{ - {[]string{"scan"},"GoKart found 0 potentially vulnerable functions", ""}, - {[]string{"scan","-r", "github.com/Contrast-Security-OSS/go-test-bench"}, "GoKart found 8 potentially vulnerable functions", cur_dir+"/go-test-bench"}, - {[]string{"scan","-r", "github.com/praetorian-inc/gokart"}, "GoKart found 0 potentially vulnerable functions", cur_dir+"/gokart"}, - {[]string{"scan", "--help"}, " -v, --verbose outputs full trace of taint analysis", ""}, - } - for _, tt := range tests { - t.Run(strings.Join(tt.args, " "), func(t *testing.T) { - - if err != nil { - t.Fatalf("Failed! %s",err) - } - - // fetch last line of output from scan command - lastline := ExecuteCommand(goKartCmd, tt.args) - //if we tested with a remote module clean it up. - if len(tt.moduledir) != 0 { - err := util.CleanupModule(tt.moduledir) - if err != nil { - fmt.Print(err) - } - } - if lastline != tt.expected_lastline { - t.Fatalf("Failed! Expected: %s\nGot: %s\n",tt.expected_lastline,lastline,) - } - }) - } -} - -func ExecuteCommand(cmd *cobra.Command,args []string) (string) { - - // change stdout to something we can read from to capture command out - // Not sure if this could potentially cause issues if buffer gets too full - old := os.Stdout - r, w, _ := os.Pipe() - os.Stdout = w - os.Stderr = w - - cmd.SetArgs(args) - Execute() - - // reset stdout to normal stdout and read output from cmd - w.Close() - stdoutres, _ := ioutil.ReadAll(r) - os.Stdout = old - - //get the last line of output for comparison with our tests - stdoutresslice := strings.Split(strings.ReplaceAll(string(stdoutres), "\r\n", "\n"), "\n") - lastline := stdoutresslice[len(stdoutresslice)-2] - return lastline - -} \ No newline at end of file +package cmd + +import ( + "fmt" + "io/ioutil" + "os" + "strings" + "testing" + + "github.com/praetorian-inc/gokart/util" + "github.com/spf13/cobra" +) + +func TestScanCommand(t *testing.T) { + // Tests the Scan command. + cur_dir, err := os.Getwd() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + fmt.Printf("Current dir is: %s", cur_dir) + var tests = []struct { + args []string + expected_lastline string + moduledir string + }{ + {[]string{"scan"}, "GoKart found 0 potentially vulnerable functions", ""}, + {[]string{"scan", "-r", "https://github.com/praetorian-inc/gokart"}, "GoKart found 0 potentially vulnerable functions", cur_dir + "/gokart"}, + {[]string{"scan", "--help"}, " -v, --verbose outputs full trace of taint analysis", ""}, + } + for _, tt := range tests { + t.Run(strings.Join(tt.args, " "), func(t *testing.T) { + + if err != nil { + t.Fatalf("Failed! %s", err) + } + + // fetch last line of output from scan command + lastline := ExecuteCommand(goKartCmd, tt.args) + //if we tested with a remote module clean it up. + if len(tt.moduledir) != 0 { + err := util.CleanupModule(tt.moduledir) + if err != nil { + fmt.Print(err) + } + } + if lastline != tt.expected_lastline { + t.Fatalf("Failed! Expected: %s\nGot: %s\n", tt.expected_lastline, lastline) + } + }) + } +} + +func ExecuteCommand(cmd *cobra.Command, args []string) string { + + // change stdout to something we can read from to capture command out + // Not sure if this could potentially cause issues if buffer gets too full + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + os.Stderr = w + + cmd.SetArgs(args) + Execute() + + // reset stdout to normal stdout and read output from cmd + w.Close() + stdoutres, _ := ioutil.ReadAll(r) + os.Stdout = old + + //get the last line of output for comparison with our tests + stdoutresslice := strings.Split(strings.ReplaceAll(string(stdoutres), "\r\n", "\n"), "\n") + lastline := stdoutresslice[len(stdoutresslice)-2] + return lastline + +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/util/config.go new/gokart-0.3.0/util/config.go --- old/gokart-0.2.0/util/config.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/util/config.go 2021-09-20 23:52:51.000000000 +0200 @@ -30,8 +30,10 @@ type ConfigType struct { GlobalsSafe bool OutputSarif bool + OutputJSON bool Debug bool Verbose bool + ExitCode bool YMLPath string OutputPath string } @@ -121,18 +123,22 @@ } // InitConfig() parses the flags and sets the corresponding Config variables -func InitConfig(globals bool, sarif bool, verbose bool, debug bool, output_path string, yml string) { +func InitConfig(globals bool, sarif bool, json bool, verbose bool, debug bool, output_path string, yml string, exitCode bool) { if yml == "" { yml = getDefaultConfigPath() } else if _, err := os.Stat(yml); err != nil { log.Fatalf("failed to find the provided config file at %s: %v", yml, err) } - fmt.Printf("Using config found at %s\n", yml) + if !(json || sarif) { + fmt.Printf("Using config found at %s\n", yml) + } Config.GlobalsSafe = !globals Config.OutputSarif = sarif + Config.OutputJSON = json Config.Debug = debug Config.Verbose = verbose + Config.ExitCode = exitCode Config.OutputPath = "" // get the absolute path of the output file to avoid issues when changing working directory for loading packages if output_path != "" { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/util/finding.go new/gokart-0.3.0/util/finding.go --- old/gokart-0.2.0/util/finding.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/util/finding.go 2021-09-20 23:52:51.000000000 +0200 @@ -16,6 +16,7 @@ import ( "fmt" + "log" "strings" "github.com/fatih/color" @@ -57,43 +58,93 @@ // if the source and sink are the same, return false and do not print out the finding return false } + // add filtering for findings with chan sources + if strings.Contains(finding.Untrusted_Source[0].SourceCode, "make(chan") { + if Config.Debug { + log.Printf("Filtering Finding for Source: %s\n", finding.Untrusted_Source[0].SourceCode) + } + return false + } return true } +func OutputFindingMetadata(results []Finding, outputColor bool) { + var ok bool + findingCounts := make(map[string]int) + + for _, finding := range results { + _, ok = findingCounts[finding.Type] + if ok { + findingCounts[finding.Type] += 1 + } else { + findingCounts[finding.Type] = 1 + } + } + + for findingType, count := range findingCounts { + if outputColor { + yellow := color.New(color.FgYellow).SprintFunc() + cyan := color.New(color.FgCyan).SprintFunc() + fmt.Printf("Identified %s potential %s\n", yellow(count), cyan(findingType)) + } else { + fmt.Printf("Identified %d potential %s\n", count, findingType) + } + } +} + // prints out a finding -func OutputFinding(finding Finding) { +func OutputFinding(finding Finding, outputColor bool) { if Config.OutputSarif { SarifRecordFinding(finding.Type, finding.message, finding.Vulnerable_Function.SourceFilename, finding.Vulnerable_Function.SourceLineNum) + } else if Config.OutputJSON { + // the JSON output is printed in OutputResults in scan.go, so nothing to do for this finding + return } else { yellow := color.New(color.FgYellow).SprintFunc() cyan := color.New(color.FgCyan).SprintFunc() green := color.New(color.FgGreen).SprintFunc() + red := color.New(color.FgRed).SprintFunc() - parentFunctionArgs := finding.Vulnerable_Function.ParentFunction - parentFunctionNoArgs := StripArguments(finding.Vulnerable_Function.ParentFunction) + sinkParentNoArgs := StripArguments(finding.Vulnerable_Function.ParentFunction) - fmt.Printf("\n(%s) %s\n\n", cyan(finding.Type), yellow(finding.message)) - fmt.Printf("%s:%d\nVulnerable Function: [ %s ]\n", finding.Vulnerable_Function.SourceFilename, finding.Vulnerable_Function.SourceLineNum, parentFunctionNoArgs) + if outputColor { + fmt.Printf("\n(%s) %s\n\n", cyan(finding.Type), yellow(finding.message)) + } else { + fmt.Printf("\n(%s) %s\n\n", finding.Type, finding.message) + } + fmt.Printf("%s:%d\nVulnerable Function: [ %s ]\n", finding.Vulnerable_Function.SourceFilename, finding.Vulnerable_Function.SourceLineNum, sinkParentNoArgs) fmt.Printf(" %d:\t%s\n", finding.Vulnerable_Function.SourceLineNum-1, GrabSourceCode(finding.Vulnerable_Function.SourceFilename, finding.Vulnerable_Function.SourceLineNum-1)) - fmt.Printf(" > %d:\t%s\n", finding.Vulnerable_Function.SourceLineNum, finding.Vulnerable_Function.SourceCode) + if outputColor { + fmt.Printf(" > %d:\t%s\n", finding.Vulnerable_Function.SourceLineNum, red(finding.Vulnerable_Function.SourceCode)) + } else { + fmt.Printf(" > %d:\t%s\n", finding.Vulnerable_Function.SourceLineNum, finding.Vulnerable_Function.SourceCode) + } fmt.Printf(" %d:\t%s\n", finding.Vulnerable_Function.SourceLineNum+1, GrabSourceCode(finding.Vulnerable_Function.SourceFilename, finding.Vulnerable_Function.SourceLineNum+1)) if finding.Untrusted_Source != nil { source := finding.Untrusted_Source[0] fmt.Printf("\n%s:%d\n", source.SourceFilename, source.SourceLineNum) - fmt.Printf("Source of Untrusted Input: [ %s ]\n", parentFunctionNoArgs) + fmt.Printf("Source of Untrusted Input: [ %s ]\n", StripArguments(source.ParentFunction)) fmt.Printf(" %d:\t%s\n", source.SourceLineNum-1, GrabSourceCode(source.SourceFilename, source.SourceLineNum-1)) - fmt.Printf(" > %d:\t%s\n", source.SourceLineNum, source.SourceCode) + if outputColor { + fmt.Printf(" > %d:\t%s\n", source.SourceLineNum, red(source.SourceCode)) + } else { + fmt.Printf(" > %d:\t%s\n", source.SourceLineNum, source.SourceCode) + } fmt.Printf(" %d:\t%s\n", source.SourceLineNum+1, GrabSourceCode(source.SourceFilename, source.SourceLineNum+1)) if Config.Verbose { - fmt.Print(green("\n############################### FULL TRACE ###############################\n")) + if outputColor { + fmt.Print(green("\n############################### FULL TRACE ###############################\n")) + } else { + fmt.Print("\n############################### FULL TRACE ###############################\n") + } fmt.Printf("\nUntrusted Input Source:") for _, source := range finding.Untrusted_Source { fmt.Printf("%s:%d:\n[ %s ]\n>>>\t%s\n", source.SourceFilename, - source.SourceLineNum, parentFunctionArgs, strings.TrimLeft(source.SourceCode, " \t")) + source.SourceLineNum, StripArguments(source.ParentFunction), strings.TrimLeft(source.SourceCode, " \t")) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gokart-0.2.0/util/module.go new/gokart-0.3.0/util/module.go --- old/gokart-0.2.0/util/module.go 2021-08-27 20:22:38.000000000 +0200 +++ new/gokart-0.3.0/util/module.go 2021-09-20 23:52:51.000000000 +0200 @@ -1,72 +1,71 @@ -// Copyright 2021 Praetorian Security, Inc. -// -// 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 util - -import ( - "github.com/go-git/go-git/v5" - "fmt" - "strings" - "os" - "errors" -) - -// CloneModule clones a remote git repository over HTTP. -func CloneModule(dir string, url string) error { - // fmt.Printf("git clone %s\n", url) - fmt.Printf("Loading new racetrack: %s\n",url) - - _, err := git.PlainClone(dir, false, &git.CloneOptions{ - URL: url, - Progress: os.Stdout, - }) - if err != nil { - return err - } - - return nil -} - -//CleanupModule attempts to delete a directory. -func CleanupModule(dir string) error { - - err := os.RemoveAll(dir) - if err != nil{ - return err - } - return nil -} - -// ParseModuleName returns a directory from a module path -func ParseModuleName(mn string) (string, error) { - - cur_dir, err := os.Getwd() - if err != nil { - return "", err - } - - if len(mn) == 0 { - return "", errors.New("No module name provided") - } - - modSlice := strings.Split(mn, "/") - if len(modSlice) <= 1 { - return "", errors.New("Invalid remote module name!\nMust be in format of: github.com/praetorian/gokart") - } - - dirName := cur_dir + "/" + modSlice[len(modSlice)-1:][0] - return dirName, nil - - -} \ No newline at end of file +// Copyright 2021 Praetorian Security, Inc. +// +// 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 util + +import ( + "log" + "os" + + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/transport/ssh" +) + +// CloneModule clones a remote git repository +// An optional keyfile may be specified for use in ssh authentication +// If quiet is true, don't print clone progress to stdout +func CloneModule(dir string, url string, branch string, keyFile string, quiet bool) error { + var cloneOptions git.CloneOptions + + cloneOptions = git.CloneOptions{ + URL: url, + } + + if !quiet { + log.Printf("Cloning new remote module: %s\n", url) + cloneOptions.Progress = os.Stdout + } + + if len(branch) != 0 { + log.Printf("Cloning with remote branch reference: %s\n", branch) + cloneOptions.ReferenceName = plumbing.NewBranchReferenceName(branch) + } + + if len(keyFile) != 0 { + _, err := os.Stat(keyFile) + if err != nil { + log.Printf("Read file %s failed %s\n", keyFile, err.Error()) + return err + } + + // Clone the given repository to the given directory (password set to "") + publicKeys, err := ssh.NewPublicKeysFromFile("git", keyFile, "") + if err != nil { + log.Printf("Generate publickeys from file %s failed: %s\n", keyFile, err.Error()) + return err + } + log.Printf("Authenticating with ssh keyfile: %s\n", keyFile) + cloneOptions.Auth = publicKeys + } + + _, err := git.PlainClone(dir, false, &cloneOptions) + return err +} + +//CleanupModule attempts to delete a directory. +func CleanupModule(dir string) error { + err := os.RemoveAll(dir) + return err +} ++++++ vendor.tar.gz ++++++