audit_test.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. package audit
  2. import (
  3. "fmt"
  4. "github.com/sergi/go-diff/diffmatchpatch"
  5. "github.com/zricethezav/gitleaks/config"
  6. "github.com/zricethezav/gitleaks/manager"
  7. "github.com/zricethezav/gitleaks/options"
  8. "io/ioutil"
  9. "os"
  10. "runtime"
  11. "strings"
  12. "testing"
  13. )
  14. const testRepoBase = "../test_data/test_repos/"
  15. func TestAudit(t *testing.T) {
  16. moveDotGit("dotGit", ".git")
  17. defer moveDotGit(".git", "dotGit")
  18. tests := []struct {
  19. description string
  20. opts options.Options
  21. wantPath string
  22. wantErr error
  23. emptyRepo bool
  24. wantEmpty bool
  25. }{
  26. {
  27. description: "test local repo one aws leak",
  28. opts: options.Options{
  29. RepoPath: "../test_data/test_repos/test_repo_1",
  30. Report: "../test_data/test_local_repo_one_aws_leak.json.got",
  31. },
  32. wantPath: "../test_data/test_local_repo_one_aws_leak.json",
  33. },
  34. {
  35. description: "test local repo one aws leak threaded",
  36. opts: options.Options{
  37. Threads: runtime.GOMAXPROCS(0),
  38. RepoPath: "../test_data/test_repos/test_repo_1",
  39. Report: "../test_data/test_local_repo_one_aws_leak.json.got",
  40. },
  41. wantPath: "../test_data/test_local_repo_one_aws_leak.json",
  42. },
  43. {
  44. description: "test non existent repo",
  45. opts: options.Options{
  46. RepoPath: "../test_data/test_repos/no_repo_here",
  47. },
  48. emptyRepo: true,
  49. },
  50. {
  51. description: "test local repo one aws leak whitelisted",
  52. opts: options.Options{
  53. RepoPath: "../test_data/test_repos/test_repo_1",
  54. Config: "../test_data/test_configs/aws_key_whitelist_python_files.toml",
  55. },
  56. wantEmpty: true,
  57. },
  58. {
  59. description: "test local repo two leaks",
  60. opts: options.Options{
  61. RepoPath: "../test_data/test_repos/test_repo_2",
  62. Report: "../test_data/test_local_repo_two_leaks.json.got",
  63. },
  64. wantPath: "../test_data/test_local_repo_two_leaks.json",
  65. },
  66. {
  67. description: "test local repo two leaks globally whitelisted",
  68. opts: options.Options{
  69. RepoPath: "../test_data/test_repos/test_repo_2",
  70. Config: "../test_data/test_configs/aws_key_global_whitelist_file.toml",
  71. },
  72. wantEmpty: true,
  73. },
  74. {
  75. description: "test local repo two leaks whitelisted",
  76. opts: options.Options{
  77. RepoPath: "../test_data/test_repos/test_repo_2",
  78. Config: "../test_data/test_configs/aws_key_whitelist_files.toml",
  79. },
  80. wantEmpty: true,
  81. },
  82. {
  83. description: "test local repo three leaks dev branch",
  84. opts: options.Options{
  85. RepoPath: "../test_data/test_repos/test_repo_3",
  86. Report: "../test_data/test_local_repo_three_leaks.json.got",
  87. Config: "../test_data/test_configs/aws_key.toml",
  88. Branch: "dev",
  89. },
  90. wantPath: "../test_data/test_local_repo_three_leaks.json",
  91. },
  92. {
  93. description: "test local repo branch does not exist",
  94. opts: options.Options{
  95. RepoPath: "../test_data/test_repos/test_repo_3",
  96. Branch: "nobranch",
  97. },
  98. wantEmpty: true,
  99. },
  100. {
  101. description: "test local repo one aws leak single commit",
  102. opts: options.Options{
  103. RepoPath: "../test_data/test_repos/test_repo_1",
  104. Report: "../test_data/test_local_repo_one_aws_leak_commit.json.got",
  105. Commit: "6557c92612d3b35979bd426d429255b3bf9fab74",
  106. },
  107. wantPath: "../test_data/test_local_repo_one_aws_leak_commit.json",
  108. },
  109. {
  110. description: "test local repo one aws leak AND leak on python files",
  111. opts: options.Options{
  112. RepoPath: "../test_data/test_repos/test_repo_1",
  113. Report: "../test_data/test_local_repo_one_aws_leak_and_file_leak.json.got",
  114. Config: "../test_data/test_configs/aws_key_file_regex.toml",
  115. },
  116. wantPath: "../test_data/test_local_repo_one_aws_leak_and_file_leak.json",
  117. },
  118. {
  119. description: "test owner path",
  120. opts: options.Options{
  121. OwnerPath: "../test_data/test_repos/",
  122. Report: "../test_data/test_local_owner_aws_leak.json.got",
  123. },
  124. wantPath: "../test_data/test_local_owner_aws_leak.json",
  125. },
  126. {
  127. description: "test entropy",
  128. opts: options.Options{
  129. RepoPath: "../test_data/test_repos/test_repo_1",
  130. Report: "../test_data/test_entropy.json.got",
  131. Config: "../test_data/test_configs/entropy.toml",
  132. },
  133. wantPath: "../test_data/test_entropy.json",
  134. },
  135. {
  136. description: "test entropy and regex",
  137. opts: options.Options{
  138. RepoPath: "../test_data/test_repos/test_repo_1",
  139. Report: "../test_data/test_regex_entropy.json.got",
  140. Config: "../test_data/test_configs/regex_entropy.toml",
  141. },
  142. wantPath: "../test_data/test_regex_entropy.json",
  143. },
  144. {
  145. description: "test local repo four entropy alternative config",
  146. opts: options.Options{
  147. RepoPath: "../test_data/test_repos/test_repo_4",
  148. Report: "../test_data/test_local_repo_four_alt_config_entropy.json.got",
  149. RepoConfig: true,
  150. },
  151. wantPath: "../test_data/test_local_repo_four_alt_config_entropy.json",
  152. },
  153. {
  154. description: "test local repo four entropy alternative config",
  155. opts: options.Options{
  156. RepoPath: "../test_data/test_repos/test_repo_1",
  157. Report: "../test_data/test_regex_whitelist.json.got",
  158. Config: "../test_data/test_configs/aws_key_aws_whitelisted.toml",
  159. },
  160. wantEmpty: true,
  161. },
  162. }
  163. for _, test := range tests {
  164. fmt.Println(test.description)
  165. cfg, err := config.NewConfig(test.opts)
  166. if err != nil {
  167. t.Error(err)
  168. }
  169. m, err := manager.NewManager(test.opts, cfg)
  170. if err != nil {
  171. t.Error(err)
  172. }
  173. err = Run(m)
  174. if test.wantErr != nil {
  175. if err == nil {
  176. t.Errorf("did not receive wantErr: %v", test.wantErr)
  177. }
  178. if err.Error() != test.wantErr.Error() {
  179. t.Errorf("wantErr does not equal err received: %v", err.Error())
  180. }
  181. continue
  182. }
  183. err = m.Report()
  184. if test.wantEmpty {
  185. if len(m.GetLeaks()) != 0 {
  186. t.Errorf("wanted no leaks but got some instead: %+v", m.GetLeaks())
  187. }
  188. continue
  189. }
  190. if test.wantPath != "" {
  191. err := fileCheck(test.wantPath, test.opts.Report)
  192. if err != nil {
  193. t.Error(err)
  194. }
  195. }
  196. }
  197. }
  198. func TestAuditUncommited(t *testing.T) {
  199. moveDotGit("dotGit", ".git")
  200. defer moveDotGit(".git", "dotGit")
  201. tests := []struct {
  202. description string
  203. opts options.Options
  204. wantPath string
  205. wantErr error
  206. emptyRepo bool
  207. wantEmpty bool
  208. fileToChange string
  209. addition string
  210. }{
  211. {
  212. description: "test audit local one leak",
  213. opts: options.Options{
  214. RepoPath: "../test_data/test_repos/test_repo_1",
  215. Report: "../test_data/test_local_repo_one_aws_leak_uncommitted.json.got",
  216. Uncommited: true,
  217. },
  218. wantPath: "../test_data/test_local_repo_one_aws_leak_uncommitted.json",
  219. fileToChange: "server.test.py",
  220. addition: " aws_access_key_id='AKIAIO5FODNN7DXAMPLE'\n\n",
  221. },
  222. {
  223. description: "test audit local no leak",
  224. opts: options.Options{
  225. RepoPath: "../test_data/test_repos/test_repo_1",
  226. Uncommited: true,
  227. },
  228. wantEmpty: true,
  229. fileToChange: "server.test.py",
  230. addition: "nothing bad",
  231. },
  232. }
  233. for _, test := range tests {
  234. fmt.Println(test.description)
  235. old, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", test.opts.RepoPath, test.fileToChange))
  236. if err != nil {
  237. t.Error(err)
  238. }
  239. altered, err := os.OpenFile(fmt.Sprintf("%s/%s", test.opts.RepoPath, test.fileToChange),
  240. os.O_WRONLY|os.O_APPEND, 0644)
  241. if err != nil {
  242. t.Error(err)
  243. }
  244. _, err = altered.WriteString(test.addition)
  245. if err != nil {
  246. t.Error(err)
  247. }
  248. cfg, err := config.NewConfig(test.opts)
  249. if err != nil {
  250. t.Error(err)
  251. }
  252. m, err := manager.NewManager(test.opts, cfg)
  253. if err != nil {
  254. t.Error(err)
  255. }
  256. if err := Run(m); err != nil {
  257. t.Error(err)
  258. }
  259. if err := m.Report(); err != nil {
  260. t.Error(err)
  261. }
  262. err = ioutil.WriteFile(fmt.Sprintf("%s/%s", test.opts.RepoPath, test.fileToChange), old, 0)
  263. if err != nil {
  264. t.Error(err)
  265. }
  266. if test.wantEmpty {
  267. continue
  268. }
  269. if test.wantPath != "" {
  270. err := fileCheck(test.wantPath, test.opts.Report)
  271. if err != nil {
  272. t.Error(err)
  273. }
  274. }
  275. }
  276. }
  277. func fileCheck(wantPath, gotPath string) error {
  278. want, err := ioutil.ReadFile(wantPath)
  279. if err != nil {
  280. return err
  281. }
  282. got, err := ioutil.ReadFile(gotPath)
  283. if err != nil {
  284. return err
  285. }
  286. if strings.Trim(string(want), "\n") != strings.Trim(string(got), "\n") {
  287. dmp := diffmatchpatch.New()
  288. diffs := dmp.DiffMain(string(want), string(got), false)
  289. return fmt.Errorf("does not equal: %s", dmp.DiffPrettyText(diffs))
  290. }
  291. if err := os.Remove(gotPath); err != nil {
  292. return err
  293. }
  294. return nil
  295. }
  296. func moveDotGit(from, to string) error {
  297. repoDirs, err := ioutil.ReadDir("../test_data/test_repos")
  298. if err != nil {
  299. return err
  300. }
  301. for _, dir := range repoDirs {
  302. if !dir.IsDir() {
  303. continue
  304. }
  305. err = os.Rename(fmt.Sprintf("%s/%s/%s", testRepoBase, dir.Name(), from),
  306. fmt.Sprintf("%s/%s/%s", testRepoBase, dir.Name(), to))
  307. if err != nil {
  308. return err
  309. }
  310. }
  311. return nil
  312. }