sarif.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. package report
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "github.com/zricethezav/gitleaks/v8/config"
  7. )
  8. func writeSarif(cfg config.Config, findings []Finding, w io.WriteCloser) error {
  9. sarif := Sarif{
  10. Schema: "https://json.schemastore.org/sarif-2.1.0.json",
  11. Version: "2.1.0",
  12. Runs: getRuns(cfg, findings),
  13. }
  14. defer w.Close()
  15. encoder := json.NewEncoder(w)
  16. encoder.SetIndent("", " ")
  17. return encoder.Encode(sarif)
  18. }
  19. func getRuns(cfg config.Config, findings []Finding) []Runs {
  20. return []Runs{
  21. {
  22. Tool: getTool(cfg),
  23. Results: getResults(findings),
  24. },
  25. }
  26. }
  27. func getTool(cfg config.Config) Tool {
  28. tool := Tool{
  29. Driver: Driver{
  30. Name: driver,
  31. SemanticVersion: version,
  32. InformationUri: "https://github.com/gitleaks/gitleaks",
  33. Rules: getRules(cfg),
  34. },
  35. }
  36. // if this tool has no rules, ensure that it is represented as [] instead of null/nil
  37. if hasEmptyRules(tool) {
  38. tool.Driver.Rules = make([]Rules, 0)
  39. }
  40. return tool
  41. }
  42. func hasEmptyRules(tool Tool) bool {
  43. return len(tool.Driver.Rules) == 0
  44. }
  45. func getRules(cfg config.Config) []Rules {
  46. // TODO for _, rule := range cfg.Rules {
  47. var rules []Rules
  48. for _, rule := range cfg.GetOrderedRules() {
  49. rules = append(rules, Rules{
  50. ID: rule.RuleID,
  51. Description: ShortDescription{
  52. Text: rule.Description,
  53. },
  54. })
  55. }
  56. return rules
  57. }
  58. func messageText(f Finding) string {
  59. if f.Commit == "" {
  60. return fmt.Sprintf("%s has detected secret for file %s.", f.RuleID, f.File)
  61. }
  62. return fmt.Sprintf("%s has detected secret for file %s at commit %s.", f.RuleID, f.File, f.Commit)
  63. }
  64. func getResults(findings []Finding) []Results {
  65. results := []Results{}
  66. for _, f := range findings {
  67. r := Results{
  68. Message: Message{
  69. Text: messageText(f),
  70. },
  71. RuleId: f.RuleID,
  72. Locations: getLocation(f),
  73. // This information goes in partial fingerprings until revision
  74. // data can be added somewhere else
  75. PartialFingerPrints: PartialFingerPrints{
  76. CommitSha: f.Commit,
  77. Email: f.Email,
  78. CommitMessage: f.Message,
  79. Date: f.Date,
  80. Author: f.Author,
  81. },
  82. Properties: Properties{
  83. Tags: f.Tags,
  84. },
  85. }
  86. results = append(results, r)
  87. }
  88. return results
  89. }
  90. func getLocation(f Finding) []Locations {
  91. uri := f.File
  92. if f.SymlinkFile != "" {
  93. uri = f.SymlinkFile
  94. }
  95. return []Locations{
  96. {
  97. PhysicalLocation: PhysicalLocation{
  98. ArtifactLocation: ArtifactLocation{
  99. URI: uri,
  100. },
  101. Region: Region{
  102. StartLine: f.StartLine,
  103. EndLine: f.EndLine,
  104. StartColumn: f.StartColumn,
  105. EndColumn: f.EndColumn,
  106. Snippet: Snippet{
  107. Text: f.Secret,
  108. },
  109. },
  110. },
  111. },
  112. }
  113. }
  114. type PartialFingerPrints struct {
  115. CommitSha string `json:"commitSha"`
  116. Email string `json:"email"`
  117. Author string `json:"author"`
  118. Date string `json:"date"`
  119. CommitMessage string `json:"commitMessage"`
  120. }
  121. type Sarif struct {
  122. Schema string `json:"$schema"`
  123. Version string `json:"version"`
  124. Runs []Runs `json:"runs"`
  125. }
  126. type ShortDescription struct {
  127. Text string `json:"text"`
  128. }
  129. type FullDescription struct {
  130. Text string `json:"text"`
  131. }
  132. type Rules struct {
  133. ID string `json:"id"`
  134. Description ShortDescription `json:"shortDescription"`
  135. }
  136. type Driver struct {
  137. Name string `json:"name"`
  138. SemanticVersion string `json:"semanticVersion"`
  139. InformationUri string `json:"informationUri"`
  140. Rules []Rules `json:"rules"`
  141. }
  142. type Tool struct {
  143. Driver Driver `json:"driver"`
  144. }
  145. type Message struct {
  146. Text string `json:"text"`
  147. }
  148. type ArtifactLocation struct {
  149. URI string `json:"uri"`
  150. }
  151. type Region struct {
  152. StartLine int `json:"startLine"`
  153. StartColumn int `json:"startColumn"`
  154. EndLine int `json:"endLine"`
  155. EndColumn int `json:"endColumn"`
  156. Snippet Snippet `json:"snippet"`
  157. }
  158. type Snippet struct {
  159. Text string `json:"text"`
  160. }
  161. type PhysicalLocation struct {
  162. ArtifactLocation ArtifactLocation `json:"artifactLocation"`
  163. Region Region `json:"region"`
  164. }
  165. type Locations struct {
  166. PhysicalLocation PhysicalLocation `json:"physicalLocation"`
  167. }
  168. type Properties struct {
  169. Tags []string `json:"tags"`
  170. }
  171. type Results struct {
  172. Message Message `json:"message"`
  173. RuleId string `json:"ruleId"`
  174. Locations []Locations `json:"locations"`
  175. PartialFingerPrints `json:"partialFingerprints"`
  176. Properties Properties `json:"properties"`
  177. }
  178. type Runs struct {
  179. Tool Tool `json:"tool"`
  180. Results []Results `json:"results"`
  181. }