sarif.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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. shortDescription := ShortDescription{
  50. Text: rule.Description,
  51. }
  52. if rule.Regex != nil {
  53. shortDescription = ShortDescription{
  54. Text: rule.Regex.String(),
  55. }
  56. } else if rule.Path != nil {
  57. shortDescription = ShortDescription{
  58. Text: rule.Path.String(),
  59. }
  60. }
  61. rules = append(rules, Rules{
  62. ID: rule.RuleID,
  63. Name: rule.Description,
  64. Description: shortDescription,
  65. })
  66. }
  67. return rules
  68. }
  69. func messageText(f Finding) string {
  70. if f.Commit == "" {
  71. return fmt.Sprintf("%s has detected secret for file %s.", f.RuleID, f.File)
  72. }
  73. return fmt.Sprintf("%s has detected secret for file %s at commit %s.", f.RuleID, f.File, f.Commit)
  74. }
  75. func getResults(findings []Finding) []Results {
  76. results := []Results{}
  77. for _, f := range findings {
  78. r := Results{
  79. Message: Message{
  80. Text: messageText(f),
  81. },
  82. RuleId: f.RuleID,
  83. Locations: getLocation(f),
  84. // This information goes in partial fingerprings until revision
  85. // data can be added somewhere else
  86. PartialFingerPrints: PartialFingerPrints{
  87. CommitSha: f.Commit,
  88. Email: f.Email,
  89. CommitMessage: f.Message,
  90. Date: f.Date,
  91. Author: f.Author,
  92. },
  93. Properties: Properties{
  94. Tags: f.Tags,
  95. },
  96. }
  97. results = append(results, r)
  98. }
  99. return results
  100. }
  101. func getLocation(f Finding) []Locations {
  102. uri := f.File
  103. if f.SymlinkFile != "" {
  104. uri = f.SymlinkFile
  105. }
  106. return []Locations{
  107. {
  108. PhysicalLocation: PhysicalLocation{
  109. ArtifactLocation: ArtifactLocation{
  110. URI: uri,
  111. },
  112. Region: Region{
  113. StartLine: f.StartLine,
  114. EndLine: f.EndLine,
  115. StartColumn: f.StartColumn,
  116. EndColumn: f.EndColumn,
  117. Snippet: Snippet{
  118. Text: f.Secret,
  119. },
  120. },
  121. },
  122. },
  123. }
  124. }
  125. type PartialFingerPrints struct {
  126. CommitSha string `json:"commitSha"`
  127. Email string `json:"email"`
  128. Author string `json:"author"`
  129. Date string `json:"date"`
  130. CommitMessage string `json:"commitMessage"`
  131. }
  132. type Sarif struct {
  133. Schema string `json:"$schema"`
  134. Version string `json:"version"`
  135. Runs []Runs `json:"runs"`
  136. }
  137. type ShortDescription struct {
  138. Text string `json:"text"`
  139. }
  140. type FullDescription struct {
  141. Text string `json:"text"`
  142. }
  143. type Rules struct {
  144. ID string `json:"id"`
  145. Name string `json:"name"`
  146. Description ShortDescription `json:"shortDescription"`
  147. }
  148. type Driver struct {
  149. Name string `json:"name"`
  150. SemanticVersion string `json:"semanticVersion"`
  151. InformationUri string `json:"informationUri"`
  152. Rules []Rules `json:"rules"`
  153. }
  154. type Tool struct {
  155. Driver Driver `json:"driver"`
  156. }
  157. type Message struct {
  158. Text string `json:"text"`
  159. }
  160. type ArtifactLocation struct {
  161. URI string `json:"uri"`
  162. }
  163. type Region struct {
  164. StartLine int `json:"startLine"`
  165. StartColumn int `json:"startColumn"`
  166. EndLine int `json:"endLine"`
  167. EndColumn int `json:"endColumn"`
  168. Snippet Snippet `json:"snippet"`
  169. }
  170. type Snippet struct {
  171. Text string `json:"text"`
  172. }
  173. type PhysicalLocation struct {
  174. ArtifactLocation ArtifactLocation `json:"artifactLocation"`
  175. Region Region `json:"region"`
  176. }
  177. type Locations struct {
  178. PhysicalLocation PhysicalLocation `json:"physicalLocation"`
  179. }
  180. type Properties struct {
  181. Tags []string `json:"tags"`
  182. }
  183. type Results struct {
  184. Message Message `json:"message"`
  185. RuleId string `json:"ruleId"`
  186. Locations []Locations `json:"locations"`
  187. PartialFingerPrints `json:"partialFingerprints"`
  188. Properties Properties `json:"properties"`
  189. }
  190. type Runs struct {
  191. Tool Tool `json:"tool"`
  192. Results []Results `json:"results"`
  193. }