detect_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. package detect
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "testing"
  7. "github.com/spf13/viper"
  8. "github.com/stretchr/testify/assert"
  9. "github.com/zricethezav/gitleaks/v8/config"
  10. "github.com/zricethezav/gitleaks/v8/report"
  11. )
  12. const configPath = "../testdata/config/"
  13. const repoBasePath = "../testdata/repos/"
  14. func TestDetect(t *testing.T) {
  15. tests := []struct {
  16. cfgName string
  17. fragment Fragment
  18. // NOTE: for expected findings, all line numbers will be 0
  19. // because line deltas are added _after_ the finding is created.
  20. // I.e, if the finding is from a --no-git file, the line number will be
  21. // increase by 1 in DetectFromFiles(). If the finding is from git,
  22. // the line number will be increased by the patch delta.
  23. expectedFindings []report.Finding
  24. wantError error
  25. }{
  26. {
  27. cfgName: "simple",
  28. fragment: Fragment{
  29. Raw: `awsToken := \"AKIALALEMEL33243OKIA\ // gitleaks:allow"`,
  30. FilePath: "tmp.go",
  31. },
  32. expectedFindings: []report.Finding{},
  33. },
  34. {
  35. cfgName: "simple",
  36. fragment: Fragment{
  37. Raw: `awsToken := \
  38. \"AKIALALEMEL33243OKIA\ // gitleaks:allow"
  39. `,
  40. FilePath: "tmp.go",
  41. },
  42. expectedFindings: []report.Finding{},
  43. },
  44. {
  45. cfgName: "simple",
  46. fragment: Fragment{
  47. Raw: `awsToken := \"AKIALALEMEL33243OKIA\"
  48. // gitleaks:allow"
  49. `,
  50. FilePath: "tmp.go",
  51. },
  52. expectedFindings: []report.Finding{
  53. {
  54. Description: "AWS Access Key",
  55. Secret: "AKIALALEMEL33243OKIA",
  56. Match: "AKIALALEMEL33243OKIA",
  57. File: "tmp.go",
  58. Line: `awsToken := \"AKIALALEMEL33243OKIA\"`,
  59. RuleID: "aws-access-key",
  60. Tags: []string{"key", "AWS"},
  61. StartLine: 0,
  62. EndLine: 0,
  63. StartColumn: 15,
  64. EndColumn: 34,
  65. Entropy: 3.1464393,
  66. },
  67. },
  68. },
  69. {
  70. cfgName: "escaped_character_group",
  71. fragment: Fragment{
  72. Raw: `pypi-AgEIcHlwaS5vcmcAAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAAB`,
  73. FilePath: "tmp.go",
  74. },
  75. expectedFindings: []report.Finding{
  76. {
  77. Description: "PyPI upload token",
  78. Secret: "pypi-AgEIcHlwaS5vcmcAAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAAB",
  79. Match: "pypi-AgEIcHlwaS5vcmcAAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAAB",
  80. Line: `pypi-AgEIcHlwaS5vcmcAAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAA-AAAAAAAAAAB`,
  81. File: "tmp.go",
  82. RuleID: "pypi-upload-token",
  83. Tags: []string{"key", "pypi"},
  84. StartLine: 0,
  85. EndLine: 0,
  86. StartColumn: 1,
  87. EndColumn: 86,
  88. Entropy: 1.9606875,
  89. },
  90. },
  91. },
  92. {
  93. cfgName: "simple",
  94. fragment: Fragment{
  95. Raw: `awsToken := \"AKIALALEMEL33243OLIA\"`,
  96. FilePath: "tmp.go",
  97. },
  98. expectedFindings: []report.Finding{
  99. {
  100. Description: "AWS Access Key",
  101. Secret: "AKIALALEMEL33243OLIA",
  102. Match: "AKIALALEMEL33243OLIA",
  103. Line: `awsToken := \"AKIALALEMEL33243OLIA\"`,
  104. File: "tmp.go",
  105. RuleID: "aws-access-key",
  106. Tags: []string{"key", "AWS"},
  107. StartLine: 0,
  108. EndLine: 0,
  109. StartColumn: 15,
  110. EndColumn: 34,
  111. Entropy: 3.0841837,
  112. },
  113. },
  114. },
  115. {
  116. cfgName: "simple",
  117. fragment: Fragment{
  118. Raw: `export BUNDLE_ENTERPRISE__CONTRIBSYS__COM=cafebabe:deadbeef;`,
  119. FilePath: "tmp.sh",
  120. },
  121. expectedFindings: []report.Finding{
  122. {
  123. Description: "Sidekiq Secret",
  124. Match: "BUNDLE_ENTERPRISE__CONTRIBSYS__COM=cafebabe:deadbeef;",
  125. Secret: "cafebabe:deadbeef",
  126. Line: `export BUNDLE_ENTERPRISE__CONTRIBSYS__COM=cafebabe:deadbeef;`,
  127. File: "tmp.sh",
  128. RuleID: "sidekiq-secret",
  129. Tags: []string{},
  130. Entropy: 2.6098502,
  131. StartLine: 0,
  132. EndLine: 0,
  133. StartColumn: 8,
  134. EndColumn: 60,
  135. },
  136. },
  137. },
  138. {
  139. cfgName: "simple",
  140. fragment: Fragment{
  141. Raw: `echo hello1; export BUNDLE_ENTERPRISE__CONTRIBSYS__COM="cafebabe:deadbeef" && echo hello2`,
  142. FilePath: "tmp.sh",
  143. },
  144. expectedFindings: []report.Finding{
  145. {
  146. Description: "Sidekiq Secret",
  147. Match: "BUNDLE_ENTERPRISE__CONTRIBSYS__COM=\"cafebabe:deadbeef\"",
  148. Secret: "cafebabe:deadbeef",
  149. File: "tmp.sh",
  150. Line: `echo hello1; export BUNDLE_ENTERPRISE__CONTRIBSYS__COM="cafebabe:deadbeef" && echo hello2`,
  151. RuleID: "sidekiq-secret",
  152. Tags: []string{},
  153. Entropy: 2.6098502,
  154. StartLine: 0,
  155. EndLine: 0,
  156. StartColumn: 21,
  157. EndColumn: 74,
  158. },
  159. },
  160. },
  161. {
  162. cfgName: "simple",
  163. fragment: Fragment{
  164. Raw: `url = "http://cafeb4b3:d3adb33f@enterprise.contribsys.com:80/path?param1=true&param2=false#heading1"`,
  165. FilePath: "tmp.sh",
  166. },
  167. expectedFindings: []report.Finding{
  168. {
  169. Description: "Sidekiq Sensitive URL",
  170. Match: "http://cafeb4b3:d3adb33f@enterprise.contribsys.com:",
  171. Secret: "cafeb4b3:d3adb33f",
  172. File: "tmp.sh",
  173. Line: `url = "http://cafeb4b3:d3adb33f@enterprise.contribsys.com:80/path?param1=true&param2=false#heading1"`,
  174. RuleID: "sidekiq-sensitive-url",
  175. Tags: []string{},
  176. Entropy: 2.984234,
  177. StartLine: 0,
  178. EndLine: 0,
  179. StartColumn: 8,
  180. EndColumn: 58,
  181. },
  182. },
  183. },
  184. {
  185. cfgName: "allow_aws_re",
  186. fragment: Fragment{
  187. Raw: `awsToken := \"AKIALALEMEL33243OLIA\"`,
  188. FilePath: "tmp.go",
  189. },
  190. expectedFindings: []report.Finding{},
  191. },
  192. {
  193. cfgName: "allow_path",
  194. fragment: Fragment{
  195. Raw: `awsToken := \"AKIALALEMEL33243OLIA\"`,
  196. FilePath: "tmp.go",
  197. },
  198. expectedFindings: []report.Finding{},
  199. },
  200. {
  201. cfgName: "allow_commit",
  202. fragment: Fragment{
  203. Raw: `awsToken := \"AKIALALEMEL33243OLIA\"`,
  204. FilePath: "tmp.go",
  205. CommitSHA: "allowthiscommit",
  206. },
  207. expectedFindings: []report.Finding{},
  208. },
  209. {
  210. cfgName: "entropy_group",
  211. fragment: Fragment{
  212. Raw: `const Discord_Public_Key = "e7322523fb86ed64c836a979cf8465fbd436378c653c1db38f9ae87bc62a6fd5"`,
  213. FilePath: "tmp.go",
  214. },
  215. expectedFindings: []report.Finding{
  216. {
  217. Description: "Discord API key",
  218. Match: "Discord_Public_Key = \"e7322523fb86ed64c836a979cf8465fbd436378c653c1db38f9ae87bc62a6fd5\"",
  219. Secret: "e7322523fb86ed64c836a979cf8465fbd436378c653c1db38f9ae87bc62a6fd5",
  220. Line: `const Discord_Public_Key = "e7322523fb86ed64c836a979cf8465fbd436378c653c1db38f9ae87bc62a6fd5"`,
  221. File: "tmp.go",
  222. RuleID: "discord-api-key",
  223. Tags: []string{},
  224. Entropy: 3.7906237,
  225. StartLine: 0,
  226. EndLine: 0,
  227. StartColumn: 7,
  228. EndColumn: 93,
  229. },
  230. },
  231. },
  232. {
  233. cfgName: "generic_with_py_path",
  234. fragment: Fragment{
  235. Raw: `const Discord_Public_Key = "e7322523fb86ed64c836a979cf8465fbd436378c653c1db38f9ae87bc62a6fd5"`,
  236. FilePath: "tmp.go",
  237. },
  238. expectedFindings: []report.Finding{},
  239. },
  240. {
  241. cfgName: "generic_with_py_path",
  242. fragment: Fragment{
  243. Raw: `const Discord_Public_Key = "e7322523fb86ed64c836a979cf8465fbd436378c653c1db38f9ae87bc62a6fd5"`,
  244. FilePath: "tmp.py",
  245. },
  246. expectedFindings: []report.Finding{
  247. {
  248. Description: "Generic API Key",
  249. Match: "Key = \"e7322523fb86ed64c836a979cf8465fbd436378c653c1db38f9ae87bc62a6fd5\"",
  250. Secret: "e7322523fb86ed64c836a979cf8465fbd436378c653c1db38f9ae87bc62a6fd5",
  251. Line: `const Discord_Public_Key = "e7322523fb86ed64c836a979cf8465fbd436378c653c1db38f9ae87bc62a6fd5"`,
  252. File: "tmp.py",
  253. RuleID: "generic-api-key",
  254. Tags: []string{},
  255. Entropy: 3.7906237,
  256. StartLine: 0,
  257. EndLine: 0,
  258. StartColumn: 22,
  259. EndColumn: 93,
  260. },
  261. },
  262. },
  263. {
  264. cfgName: "path_only",
  265. fragment: Fragment{
  266. Raw: `const Discord_Public_Key = "e7322523fb86ed64c836a979cf8465fbd436378c653c1db38f9ae87bc62a6fd5"`,
  267. FilePath: "tmp.py",
  268. },
  269. expectedFindings: []report.Finding{
  270. {
  271. Description: "Python Files",
  272. Match: "file detected: tmp.py",
  273. File: "tmp.py",
  274. RuleID: "python-files-only",
  275. Tags: []string{},
  276. },
  277. },
  278. },
  279. {
  280. cfgName: "bad_entropy_group",
  281. fragment: Fragment{
  282. Raw: `const Discord_Public_Key = "e7322523fb86ed64c836a979cf8465fbd436378c653c1db38f9ae87bc62a6fd5"`,
  283. FilePath: "tmp.go",
  284. },
  285. expectedFindings: []report.Finding{},
  286. wantError: fmt.Errorf("Discord API key invalid regex secret group 5, max regex secret group 3"),
  287. },
  288. {
  289. cfgName: "simple",
  290. fragment: Fragment{
  291. Raw: `awsToken := \"AKIALALEMEL33243OLIA\"`,
  292. FilePath: filepath.Join(configPath, "simple.toml"),
  293. },
  294. expectedFindings: []report.Finding{},
  295. },
  296. {
  297. cfgName: "allow_global_aws_re",
  298. fragment: Fragment{
  299. Raw: `awsToken := \"AKIALALEMEL33243OLIA\"`,
  300. FilePath: "tmp.go",
  301. },
  302. expectedFindings: []report.Finding{},
  303. },
  304. {
  305. cfgName: "generic_with_py_path",
  306. fragment: Fragment{
  307. Raw: `const Discord_Public_Key = "load2523fb86ed64c836a979cf8465fbd436378c653c1db38f9ae87bc62a6fd5"`,
  308. FilePath: "tmp.py",
  309. },
  310. expectedFindings: []report.Finding{},
  311. },
  312. }
  313. for _, tt := range tests {
  314. viper.Reset()
  315. viper.AddConfigPath(configPath)
  316. viper.SetConfigName(tt.cfgName)
  317. viper.SetConfigType("toml")
  318. err := viper.ReadInConfig()
  319. if err != nil {
  320. t.Error(err)
  321. }
  322. var vc config.ViperConfig
  323. err = viper.Unmarshal(&vc)
  324. if err != nil {
  325. t.Error(err)
  326. }
  327. cfg, err := vc.Translate()
  328. cfg.Path = filepath.Join(configPath, tt.cfgName+".toml")
  329. if tt.wantError != nil {
  330. if err == nil {
  331. t.Errorf("expected error")
  332. }
  333. assert.Equal(t, tt.wantError, err)
  334. }
  335. d := NewDetector(cfg)
  336. findings := d.Detect(tt.fragment)
  337. assert.ElementsMatch(t, tt.expectedFindings, findings)
  338. }
  339. }
  340. // TestFromGit tests the FromGit function
  341. func TestFromGit(t *testing.T) {
  342. tests := []struct {
  343. cfgName string
  344. source string
  345. logOpts string
  346. expectedFindings []report.Finding
  347. }{
  348. {
  349. source: filepath.Join(repoBasePath, "small"),
  350. cfgName: "simple",
  351. expectedFindings: []report.Finding{
  352. {
  353. Description: "AWS Access Key",
  354. StartLine: 20,
  355. EndLine: 20,
  356. StartColumn: 19,
  357. EndColumn: 38,
  358. Line: "\n awsToken := \"AKIALALEMEL33243OLIA\"",
  359. Secret: "AKIALALEMEL33243OLIA",
  360. Match: "AKIALALEMEL33243OLIA",
  361. File: "main.go",
  362. Date: "2021-11-02T23:37:53Z",
  363. Commit: "1b6da43b82b22e4eaa10bcf8ee591e91abbfc587",
  364. Author: "Zachary Rice",
  365. Email: "zricer@protonmail.com",
  366. Message: "Accidentally add a secret",
  367. RuleID: "aws-access-key",
  368. Tags: []string{"key", "AWS"},
  369. Entropy: 3.0841837,
  370. Fingerprint: "1b6da43b82b22e4eaa10bcf8ee591e91abbfc587:main.go:aws-access-key:20",
  371. },
  372. {
  373. Description: "AWS Access Key",
  374. StartLine: 9,
  375. EndLine: 9,
  376. StartColumn: 17,
  377. EndColumn: 36,
  378. Secret: "AKIALALEMEL33243OLIA",
  379. Match: "AKIALALEMEL33243OLIA",
  380. Line: "\n\taws_token := \"AKIALALEMEL33243OLIA\"",
  381. File: "foo/foo.go",
  382. Date: "2021-11-02T23:48:06Z",
  383. Commit: "491504d5a31946ce75e22554cc34203d8e5ff3ca",
  384. Author: "Zach Rice",
  385. Email: "zricer@protonmail.com",
  386. Message: "adding foo package with secret",
  387. RuleID: "aws-access-key",
  388. Tags: []string{"key", "AWS"},
  389. Entropy: 3.0841837,
  390. Fingerprint: "491504d5a31946ce75e22554cc34203d8e5ff3ca:foo/foo.go:aws-access-key:9",
  391. },
  392. },
  393. },
  394. {
  395. source: filepath.Join(repoBasePath, "small"),
  396. logOpts: "--all foo...",
  397. cfgName: "simple",
  398. expectedFindings: []report.Finding{
  399. {
  400. Description: "AWS Access Key",
  401. StartLine: 9,
  402. EndLine: 9,
  403. StartColumn: 17,
  404. EndColumn: 36,
  405. Secret: "AKIALALEMEL33243OLIA",
  406. Line: "\n\taws_token := \"AKIALALEMEL33243OLIA\"",
  407. Match: "AKIALALEMEL33243OLIA",
  408. Date: "2021-11-02T23:48:06Z",
  409. File: "foo/foo.go",
  410. Commit: "491504d5a31946ce75e22554cc34203d8e5ff3ca",
  411. Author: "Zach Rice",
  412. Email: "zricer@protonmail.com",
  413. Message: "adding foo package with secret",
  414. RuleID: "aws-access-key",
  415. Tags: []string{"key", "AWS"},
  416. Entropy: 3.0841837,
  417. Fingerprint: "491504d5a31946ce75e22554cc34203d8e5ff3ca:foo/foo.go:aws-access-key:9",
  418. },
  419. },
  420. },
  421. }
  422. err := moveDotGit("dotGit", ".git")
  423. if err != nil {
  424. t.Fatal(err)
  425. }
  426. defer func() {
  427. if err := moveDotGit(".git", "dotGit"); err != nil {
  428. t.Error(err)
  429. }
  430. }()
  431. for _, tt := range tests {
  432. viper.AddConfigPath(configPath)
  433. viper.SetConfigName("simple")
  434. viper.SetConfigType("toml")
  435. err = viper.ReadInConfig()
  436. if err != nil {
  437. t.Error(err)
  438. }
  439. var vc config.ViperConfig
  440. err = viper.Unmarshal(&vc)
  441. if err != nil {
  442. t.Error(err)
  443. }
  444. cfg, err := vc.Translate()
  445. if err != nil {
  446. t.Error(err)
  447. }
  448. detector := NewDetector(cfg)
  449. findings, err := detector.DetectGit(tt.source, tt.logOpts, DetectType)
  450. if err != nil {
  451. t.Error(err)
  452. }
  453. for _, f := range findings {
  454. f.Match = "" // remove lines cause copying and pasting them has some wack formatting
  455. }
  456. assert.ElementsMatch(t, tt.expectedFindings, findings)
  457. }
  458. }
  459. // TestFromFiles tests the FromFiles function
  460. func TestFromFiles(t *testing.T) {
  461. tests := []struct {
  462. cfgName string
  463. source string
  464. expectedFindings []report.Finding
  465. }{
  466. {
  467. source: filepath.Join(repoBasePath, "nogit"),
  468. cfgName: "simple",
  469. expectedFindings: []report.Finding{
  470. {
  471. Description: "AWS Access Key",
  472. StartLine: 20,
  473. EndLine: 20,
  474. StartColumn: 16,
  475. EndColumn: 35,
  476. Match: "AKIALALEMEL33243OLIA",
  477. Secret: "AKIALALEMEL33243OLIA",
  478. Line: "\n\tawsToken := \"AKIALALEMEL33243OLIA\"",
  479. File: "../testdata/repos/nogit/main.go",
  480. SymlinkFile: "",
  481. RuleID: "aws-access-key",
  482. Tags: []string{"key", "AWS"},
  483. Entropy: 3.0841837,
  484. Fingerprint: "../testdata/repos/nogit/main.go:aws-access-key:20",
  485. },
  486. },
  487. },
  488. {
  489. source: filepath.Join(repoBasePath, "nogit", "main.go"),
  490. cfgName: "simple",
  491. expectedFindings: []report.Finding{
  492. {
  493. Description: "AWS Access Key",
  494. StartLine: 20,
  495. EndLine: 20,
  496. StartColumn: 16,
  497. EndColumn: 35,
  498. Match: "AKIALALEMEL33243OLIA",
  499. Secret: "AKIALALEMEL33243OLIA",
  500. Line: "\n\tawsToken := \"AKIALALEMEL33243OLIA\"",
  501. File: "../testdata/repos/nogit/main.go",
  502. RuleID: "aws-access-key",
  503. Tags: []string{"key", "AWS"},
  504. Entropy: 3.0841837,
  505. Fingerprint: "../testdata/repos/nogit/main.go:aws-access-key:20",
  506. },
  507. },
  508. },
  509. }
  510. for _, tt := range tests {
  511. viper.AddConfigPath(configPath)
  512. viper.SetConfigName("simple")
  513. viper.SetConfigType("toml")
  514. err := viper.ReadInConfig()
  515. if err != nil {
  516. t.Error(err)
  517. }
  518. var vc config.ViperConfig
  519. err = viper.Unmarshal(&vc)
  520. if err != nil {
  521. t.Error(err)
  522. }
  523. cfg, _ := vc.Translate()
  524. detector := NewDetector(cfg)
  525. detector.FollowSymlinks = true
  526. findings, err := detector.DetectFiles(tt.source)
  527. if err != nil {
  528. t.Error(err)
  529. }
  530. assert.ElementsMatch(t, tt.expectedFindings, findings)
  531. }
  532. }
  533. func TestDetectWithSymlinks(t *testing.T) {
  534. tests := []struct {
  535. cfgName string
  536. source string
  537. expectedFindings []report.Finding
  538. }{
  539. {
  540. source: filepath.Join(repoBasePath, "symlinks/file_symlink"),
  541. cfgName: "simple",
  542. expectedFindings: []report.Finding{
  543. {
  544. Description: "Asymmetric Private Key",
  545. StartLine: 1,
  546. EndLine: 1,
  547. StartColumn: 1,
  548. EndColumn: 35,
  549. Match: "-----BEGIN OPENSSH PRIVATE KEY-----",
  550. Secret: "-----BEGIN OPENSSH PRIVATE KEY-----",
  551. Line: "-----BEGIN OPENSSH PRIVATE KEY-----",
  552. File: "../testdata/repos/symlinks/source_file/id_ed25519",
  553. SymlinkFile: "../testdata/repos/symlinks/file_symlink/symlinked_id_ed25519",
  554. RuleID: "apkey",
  555. Tags: []string{"key", "AsymmetricPrivateKey"},
  556. Entropy: 3.587164,
  557. Fingerprint: "../testdata/repos/symlinks/source_file/id_ed25519:apkey:1",
  558. },
  559. },
  560. },
  561. }
  562. for _, tt := range tests {
  563. viper.AddConfigPath(configPath)
  564. viper.SetConfigName("simple")
  565. viper.SetConfigType("toml")
  566. err := viper.ReadInConfig()
  567. if err != nil {
  568. t.Error(err)
  569. }
  570. var vc config.ViperConfig
  571. err = viper.Unmarshal(&vc)
  572. if err != nil {
  573. t.Error(err)
  574. }
  575. cfg, _ := vc.Translate()
  576. detector := NewDetector(cfg)
  577. detector.FollowSymlinks = true
  578. findings, err := detector.DetectFiles(tt.source)
  579. if err != nil {
  580. t.Error(err)
  581. }
  582. assert.ElementsMatch(t, tt.expectedFindings, findings)
  583. }
  584. }
  585. func moveDotGit(from, to string) error {
  586. repoDirs, err := os.ReadDir("../testdata/repos")
  587. if err != nil {
  588. return err
  589. }
  590. for _, dir := range repoDirs {
  591. if to == ".git" {
  592. _, err := os.Stat(fmt.Sprintf("%s/%s/%s", repoBasePath, dir.Name(), "dotGit"))
  593. if os.IsNotExist(err) {
  594. // dont want to delete the only copy of .git accidentally
  595. continue
  596. }
  597. os.RemoveAll(fmt.Sprintf("%s/%s/%s", repoBasePath, dir.Name(), ".git"))
  598. }
  599. if !dir.IsDir() {
  600. continue
  601. }
  602. _, err := os.Stat(fmt.Sprintf("%s/%s/%s", repoBasePath, dir.Name(), from))
  603. if os.IsNotExist(err) {
  604. continue
  605. }
  606. err = os.Rename(fmt.Sprintf("%s/%s/%s", repoBasePath, dir.Name(), from),
  607. fmt.Sprintf("%s/%s/%s", repoBasePath, dir.Name(), to))
  608. if err != nil {
  609. return err
  610. }
  611. }
  612. return nil
  613. }