فهرست منبع

Sarif (#421)

* init

* more work

* more sarif work

* mergin
Zachary Rice 5 سال پیش
والد
کامیت
ef4655fcf1
7فایلهای تغییر یافته به همراه240 افزوده شده و 50 حذف شده
  1. 5 5
      config/config.go
  2. 1 1
      config/config_test.go
  3. 0 41
      manager/manager.go
  4. 78 0
      manager/report.go
  5. 153 0
      manager/sarif.go
  6. 1 1
      options/options.go
  7. 2 2
      scan/rule.go

+ 5 - 5
config/config.go

@@ -14,7 +14,7 @@ import (
 
 // AllowList is struct containing items that if encountered will allowlist
 // a commit/line of code that would be considered a leak.
-type Allowlist struct {
+type AllowList struct {
 	Description string
 	Regexes     []*regexp.Regexp
 	Commits     []string
@@ -41,7 +41,7 @@ type Rule struct {
 	File        *regexp.Regexp
 	Path        *regexp.Regexp
 	Tags        []string
-	Allowlist   Allowlist
+	AllowList   AllowList
 	Entropies   []Entropy
 }
 
@@ -49,7 +49,7 @@ type Rule struct {
 // Each Rule contains a description, regular expression, tags, and allowlists if available
 type Config struct {
 	Rules     []Rule
-	Allowlist Allowlist
+	Allowlist AllowList
 }
 
 // TomlAllowList is a struct used in the TomlLoader that loads in allowlists from
@@ -135,7 +135,7 @@ func (tomlLoader TomlLoader) Parse() (Config, error) {
 		}
 
 		// rule specific allowlists
-		var allowList Allowlist
+		var allowList AllowList
 
 		// rule specific regexes
 		for _, re := range rule.AllowList.Regexes {
@@ -199,7 +199,7 @@ func (tomlLoader TomlLoader) Parse() (Config, error) {
 			File:        fileNameRe,
 			Path:        filePathRe,
 			Tags:        rule.Tags,
-			Allowlist:   allowList,
+			AllowList:   allowList,
 			Entropies:   entropies,
 		}
 

+ 1 - 1
config/config_test.go

@@ -15,7 +15,7 @@ func TestParse(t *testing.T) {
 		wantErr       error
 		wantFileRegex *regexp.Regexp
 		wantMessages  *regexp.Regexp
-		wantAllowlist Allowlist
+		wantAllowlist AllowList
 	}{
 		{
 			description: "default config",

+ 0 - 41
manager/manager.go

@@ -2,7 +2,6 @@ package manager
 
 import (
 	"crypto/sha1"
-	"encoding/csv"
 	"encoding/hex"
 	"encoding/json"
 	"fmt"
@@ -261,46 +260,6 @@ func (manager *Manager) DebugOutput() {
 
 }
 
-// Report saves gitleaks leaks to a json specified by --report={report.json}
-func (manager *Manager) Report() error {
-	close(manager.leakChan)
-	close(manager.metadata.timings)
-
-	if log.IsLevelEnabled(log.DebugLevel) {
-		manager.DebugOutput()
-	}
-
-	if manager.Opts.Report != "" {
-		if len(manager.GetLeaks()) == 0 {
-			log.Infof("no leaks found, skipping writing report")
-			return nil
-		}
-		file, err := os.Create(manager.Opts.Report)
-		if err != nil {
-			return err
-		}
-
-		if manager.Opts.ReportFormat == "json" {
-			encoder := json.NewEncoder(file)
-			encoder.SetIndent("", " ")
-			err = encoder.Encode(manager.leaks)
-			if err != nil {
-				return err
-			}
-		} else {
-			w := csv.NewWriter(file)
-			_ = w.Write([]string{"repo", "line", "commit", "offender", "rule", "tags", "commitMsg", "author", "email", "file", "date"})
-			for _, leak := range manager.GetLeaks() {
-				w.Write([]string{leak.Repo, leak.Line, leak.Commit, leak.Offender, leak.Rule, leak.Tags, leak.Message, leak.Author, leak.Email, leak.File, leak.Date.Format(time.RFC3339)})
-			}
-			w.Flush()
-		}
-		_ = file.Close()
-
-		log.Infof("report written to %s", manager.Opts.Report)
-	}
-	return nil
-}
 
 func (manager *Manager) receiveInterrupt() {
 	<-manager.stopChan

+ 78 - 0
manager/report.go

@@ -0,0 +1,78 @@
+package manager
+
+import (
+	"encoding/csv"
+	"encoding/json"
+	"os"
+	"time"
+
+	"github.com/zricethezav/gitleaks/v6/version"
+
+	log "github.com/sirupsen/logrus"
+)
+
+// Report saves gitleaks leaks to a json specified by --report={report.json}
+func (manager *Manager) Report() error {
+	close(manager.leakChan)
+	close(manager.metadata.timings)
+
+	if log.IsLevelEnabled(log.DebugLevel) {
+		manager.DebugOutput()
+	}
+
+	if manager.Opts.Report != "" {
+		if len(manager.GetLeaks()) == 0 {
+			log.Infof("no leaks found, skipping writing report")
+			return nil
+		}
+		file, err := os.Create(manager.Opts.Report)
+		if err != nil {
+			return err
+		}
+
+		switch manager.Opts.ReportFormat {
+		case "json":
+			encoder := json.NewEncoder(file)
+			encoder.SetIndent("", " ")
+			err = encoder.Encode(manager.leaks)
+			if err != nil {
+				return err
+			}
+		case "csv":
+			w := csv.NewWriter(file)
+			_ = w.Write([]string{"repo", "line", "commit", "offender", "rule", "tags", "commitMsg", "author", "email", "file", "date"})
+			for _, leak := range manager.GetLeaks() {
+				w.Write([]string{leak.Repo, leak.Line, leak.Commit, leak.Offender, leak.Rule, leak.Tags, leak.Message, leak.Author, leak.Email, leak.File, leak.Date.Format(time.RFC3339)})
+			}
+			w.Flush()
+		case "sarif":
+			s := Sarif{
+				Schema:  "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json",
+				Version: "2.1.0",
+				Runs: []Runs{
+					{
+						Tool: Tool{
+							Driver: Driver{
+								Name:            "Gitleaks",
+								SemanticVersion: version.Version,
+								Rules:           manager.configToRules(),
+							},
+						},
+						Results: manager.leaksToResults(),
+					},
+				},
+			}
+			encoder := json.NewEncoder(file)
+			encoder.SetIndent("", " ")
+			err = encoder.Encode(s)
+			if err != nil {
+				return err
+			}
+		}
+		_ = file.Close()
+
+		log.Infof("report written to %s", manager.Opts.Report)
+	}
+	return nil
+}
+

+ 153 - 0
manager/sarif.go

@@ -0,0 +1,153 @@
+package manager
+
+import (
+	"fmt"
+	"time"
+)
+
+//Sarif ...
+type Sarif struct {
+	Schema  string `json:"$schema"`
+	Version string `json:"version"`
+	Runs    []Runs `json:"runs"`
+}
+
+//ShortDescription ...
+type ShortDescription struct {
+	Text string `json:"text"`
+}
+
+//FullDescription ...
+type FullDescription struct {
+	Text string `json:"text"`
+}
+
+//Rules ...
+type Rules struct {
+	ID         string          `json:"id"`
+	Name       string          `json:"name"`
+}
+
+//Driver ...
+type Driver struct {
+	Name            string  `json:"name"`
+	SemanticVersion string  `json:"semanticVersion"`
+	Rules           []Rules `json:"rules"`
+}
+
+//Tool ...
+type Tool struct {
+	Driver Driver `json:"driver"`
+}
+
+//Message ...
+type Message struct {
+	Text string `json:"text"`
+}
+
+//ArtifactLocation ...
+type ArtifactLocation struct {
+	URI string `json:"uri"`
+}
+
+//Region ...
+type Region struct {
+	StartLine int     `json:"startLine"`
+	Snippet   Snippet `json:"snippet"`
+}
+
+//Snippet ...
+type Snippet struct {
+	Text string `json:"text"`
+}
+
+//PhysicalLocation ...
+type PhysicalLocation struct {
+	ArtifactLocation ArtifactLocation `json:"artifactLocation"`
+	Region           Region           `json:"region"`
+}
+
+//Locations ...
+type Locations struct {
+	PhysicalLocation PhysicalLocation `json:"physicalLocation"`
+}
+
+//Results ...
+type Results struct {
+	Message    Message          `json:"message"`
+	Properties ResultProperties `json:"properties"`
+	Locations  []Locations      `json:"locations"`
+}
+
+//ResultProperties ...
+type ResultProperties struct {
+	Commit        string    `json:"commit"`
+	Offender      string    `json:"offender"`
+	Date          time.Time `json:"date"`
+	Author        string    `json:"author"`
+	Email         string    `json:"email"`
+	CommitMessage string    `json:"commitMessage"`
+	Operation     string    `json:"gitOperation"`
+	Repo          string    `json:"repo"`
+}
+
+//Runs ...
+type Runs struct {
+	Tool    Tool      `json:"tool"`
+	Results []Results `json:"results"`
+}
+
+func (manager *Manager) configToRules() []Rules {
+	var rules []Rules
+	for _, rule := range manager.Config.Rules {
+		rules = append(rules, Rules{
+			ID:   rule.Description,
+			Name: rule.Description,
+		})
+	}
+	return rules
+}
+
+func (manager *Manager) leaksToResults() []Results {
+	var results []Results
+	for _, leak := range manager.leaks {
+		results = append(results, Results{
+			Message: Message{
+				Text: fmt.Sprintf("%s secret detected", leak.Rule),
+			},
+			Properties: ResultProperties{
+				Commit:        leak.Commit,
+				Offender:      leak.Offender,
+				Date:          leak.Date,
+				Author:        leak.Author,
+				Email:         leak.Email,
+				CommitMessage: leak.Message,
+				Operation:     leak.Operation,
+				Repo:          leak.Repo,
+			},
+			Locations: leakToLocation(leak),
+		})
+	}
+
+	return results
+}
+
+func leakToLocation(leak Leak) []Locations {
+	return []Locations{
+		{
+			PhysicalLocation:
+			PhysicalLocation{
+				ArtifactLocation: ArtifactLocation{
+					URI: leak.File,
+				},
+				Region: Region{
+					StartLine: leak.LineNumber,
+					Snippet: Snippet{
+						Text: leak.Line,
+					},
+				},
+			},
+		},
+	}
+}
+

+ 1 - 1
options/options.go

@@ -45,7 +45,7 @@ type Options struct {
 	OwnerPath     string `long:"owner-path" description:"Path to owner directory (repos discovered)"`
 	Branch        string `long:"branch" description:"Branch to scan"`
 	Report        string `long:"report" description:"path to write json leaks file"`
-	ReportFormat  string `long:"report-format" default:"json" description:"json or csv"`
+	ReportFormat  string `long:"report-format" default:"json" description:"json, csv, sarif"`
 	Redact        bool   `long:"redact" description:"redact secrets from log messages and leaks"`
 	Debug         bool   `long:"debug" description:"log debug messages"`
 	RepoConfig    bool   `long:"repo-config" description:"Load config from target repo. Config file must be \".gitleaks.toml\" or \"gitleaks.toml\""`

+ 2 - 2
scan/rule.go

@@ -58,7 +58,7 @@ func (repo *Repo) CheckRules(bundle *Bundle) {
 		start := time.Now()
 
 		// For each rule we want to check filename allowlists
-		if isAllowListed(filename, rule.Allowlist.Files) || isAllowListed(path, rule.Allowlist.Paths) {
+		if isAllowListed(filename, rule.AllowList.Files) || isAllowListed(path, rule.AllowList.Paths) {
 			continue
 		}
 
@@ -112,7 +112,7 @@ func (repo *Repo) CheckRules(bundle *Bundle) {
 					offender := bundle.Content[loc[0]:loc[1]]
 					groups := rule.Regex.FindStringSubmatch(offender)
 
-					if isAllowListed(line, append(rule.Allowlist.Regexes, repo.config.Allowlist.Regexes...)) {
+					if isAllowListed(line, append(rule.AllowList.Regexes, repo.config.Allowlist.Regexes...)) {
 						continue
 					}