allowlist_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. package config
  2. import (
  3. "errors"
  4. "testing"
  5. "github.com/google/go-cmp/cmp"
  6. "github.com/google/go-cmp/cmp/cmpopts"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/zricethezav/gitleaks/v8/regexp"
  9. )
  10. func TestCommitAllowed(t *testing.T) {
  11. tests := []struct {
  12. allowlist Allowlist
  13. commit string
  14. commitAllowed bool
  15. }{
  16. {
  17. allowlist: Allowlist{
  18. Commits: []string{"commitA"},
  19. },
  20. commit: "commitA",
  21. commitAllowed: true,
  22. },
  23. {
  24. allowlist: Allowlist{
  25. Commits: []string{"commitB"},
  26. },
  27. commit: "commitA",
  28. commitAllowed: false,
  29. },
  30. {
  31. allowlist: Allowlist{
  32. Commits: []string{"commitB"},
  33. },
  34. commit: "",
  35. commitAllowed: false,
  36. },
  37. }
  38. for _, tt := range tests {
  39. isAllowed, _ := tt.allowlist.CommitAllowed(tt.commit)
  40. assert.Equal(t, tt.commitAllowed, isAllowed)
  41. }
  42. }
  43. func TestRegexAllowed(t *testing.T) {
  44. tests := []struct {
  45. allowlist Allowlist
  46. secret string
  47. regexAllowed bool
  48. }{
  49. {
  50. allowlist: Allowlist{
  51. Regexes: []*regexp.Regexp{regexp.MustCompile("matchthis")},
  52. },
  53. secret: "a secret: matchthis, done",
  54. regexAllowed: true,
  55. },
  56. {
  57. allowlist: Allowlist{
  58. Regexes: []*regexp.Regexp{regexp.MustCompile("matchthis")},
  59. },
  60. secret: "a secret",
  61. regexAllowed: false,
  62. },
  63. }
  64. for _, tt := range tests {
  65. assert.Equal(t, tt.regexAllowed, tt.allowlist.RegexAllowed(tt.secret))
  66. }
  67. }
  68. func TestPathAllowed(t *testing.T) {
  69. tests := []struct {
  70. allowlist Allowlist
  71. path string
  72. pathAllowed bool
  73. }{
  74. {
  75. allowlist: Allowlist{
  76. Paths: []*regexp.Regexp{regexp.MustCompile("path")},
  77. },
  78. path: "a path",
  79. pathAllowed: true,
  80. },
  81. {
  82. allowlist: Allowlist{
  83. Paths: []*regexp.Regexp{regexp.MustCompile("path")},
  84. },
  85. path: "a ???",
  86. pathAllowed: false,
  87. },
  88. }
  89. for _, tt := range tests {
  90. assert.Equal(t, tt.pathAllowed, tt.allowlist.PathAllowed(tt.path))
  91. }
  92. }
  93. func TestValidate(t *testing.T) {
  94. tests := map[string]struct {
  95. input Allowlist
  96. expected Allowlist
  97. wantErr error
  98. }{
  99. "empty conditions": {
  100. input: Allowlist{},
  101. wantErr: errors.New("must contain at least one check for: commits, paths, regexes, or stopwords"),
  102. },
  103. "deduplicated commits and stopwords": {
  104. input: Allowlist{
  105. Commits: []string{"commitA", "commitB", "commitA"},
  106. StopWords: []string{"stopwordA", "stopwordB", "stopwordA"},
  107. },
  108. expected: Allowlist{
  109. Commits: []string{"commita", "commitb"},
  110. StopWords: []string{"stopworda", "stopwordb"},
  111. },
  112. },
  113. }
  114. for _, tt := range tests {
  115. // Expected an error.
  116. err := tt.input.Validate()
  117. if err != nil {
  118. if tt.wantErr == nil {
  119. t.Fatalf("Received unexpected error: %v", err)
  120. } else if !assert.EqualError(t, err, tt.wantErr.Error()) {
  121. t.Fatalf("Received unexpected error, expected '%v', got '%v'", tt.wantErr, err)
  122. }
  123. } else {
  124. if tt.wantErr != nil {
  125. t.Fatalf("Did not receive expected error: %v", tt.wantErr)
  126. }
  127. }
  128. var (
  129. regexComparer = func(x, y *regexp.Regexp) bool {
  130. // Compare the string representation of the regex patterns.
  131. if x == nil || y == nil {
  132. return x == y
  133. }
  134. return x.String() == y.String()
  135. }
  136. arrayComparer = func(a, b string) bool {
  137. return a < b
  138. }
  139. opts = cmp.Options{
  140. cmp.Comparer(regexComparer),
  141. cmpopts.SortSlices(arrayComparer),
  142. cmpopts.IgnoreUnexported(Allowlist{}),
  143. }
  144. )
  145. if diff := cmp.Diff(tt.input, tt.expected, opts); diff != "" {
  146. t.Errorf("diff: (-want +got)\n%s", diff)
  147. }
  148. }
  149. }
  150. var benchCommitAllowlist = func() *Allowlist {
  151. allowlist := Allowlist{
  152. Commits: []string{
  153. "ba1beca8ac634d8b202e0daffebecceeb6dda2fc",
  154. "74b19abcd33ff3a6cac8aebdcfccacde9ad40f5c",
  155. "11ed23ff2df37b2c114ac13dbd902e3bdb8b9c63",
  156. "d39bcd0ebd3c9fb8d2f1bfbc98fc92bceeb3eed3",
  157. "faf321cf8f9f2cf654fab12f72ef64e6ffb7237e",
  158. "5486ce1abcdfcc862efeb6f5071a1ddef0fb3926",
  159. "fbcfdbdffdabf1c0f627ecdfcda91ca8c1b533ce",
  160. "3db86dce50f1d99fe820a4bf42ffedaefedb1bcf",
  161. "18ccec6cb316ce1abed9e8ec262ecd3dd4a3e222",
  162. "20f1cc29eaa028e7e6bcdc82f2103c1af3899c5c",
  163. "d90b5572e9bdb0cebf5edc3ad412c38ecd6b8ca6",
  164. "a0c7a0addf82d2a84babdd8be1f3cee887cb69cf",
  165. "0c3c967d21bca179e2d60b738fab848be9a65a45",
  166. "85cb3ac8817afad5ffde45a29036f4a1fa8af2eb",
  167. "cb9c19a9dcb05bebca7dacbed9b8afefeb2dc5ad",
  168. "4bac7b9fbdc851caf3c5f0cece85acaa3ebb7fd2",
  169. "ad2bfd0c271bbb1ac991fdfefcabb6dcc43cb3f5",
  170. "0c522c2750ec2fdf134721ea6000841fed5ffdd1",
  171. "fedbf91dddc017f9994edc7f3a75dcd9eabfdd2c",
  172. "9e5205bdeb080d84bfb25dfcc86dccc3fe5be499",
  173. "b5476d7901b105c2fdb5fc87a9f4fabdb40daf81",
  174. "f8fdbc509fb3c3ef1c1cbe8e70ac933ac4109cc3",
  175. "92727ea97463cd55eed3069ab54483ba20b94f3c",
  176. "e4a01ce6fdb6f6a39d2c781f7dcba3ae37973d0b",
  177. "dcdd273bdf8159faf60bfca6b2a20a98a9985cf3",
  178. "3f1efb9b7705cc1cae9ebeafa8eaf95ab79d6bea",
  179. "20831ce7da9e2cb78ad8cd2a73ca693d8ffaee9c",
  180. "edffec40aa85fbf947b20b3ffcfe6e7f6a94d017",
  181. "cfbbc60acefdc1f04ad26e022fba21f72ce5c25b",
  182. "3beac0da108cbb4fac1ebcd060ba2b67aafbefdd",
  183. "21a87495e1f1f4ee9d9dfab1c3e5ffbe4b6e7dba",
  184. "a2a3e8ba9dcae74d7bc1267f07fec3a44a0c9b08",
  185. "abe2c2ff0a9befc1cbae77ea6aebf8d3a46ee68f",
  186. "9f9e4cea3bee21deb769f830bcf76185eba76ab6",
  187. "48e2f92ccdd1cb33e10eba117386cb054e06f2e0",
  188. "4e10cc0ae797adce6cec0beda8782b28ba2dcf8c",
  189. "7d6eb5bc7bbee783ab012ee290f8b9acabe38367",
  190. "bc5d1b99fd2cbacb225e574fd05c57a3f44ca35c",
  191. "9a3ea6fd3dcfd135ea3b9bc63ab6494f5c54faaf",
  192. "fc62eec1f10e646ec9a8dacaddf1becae7e31823",
  193. "fd01d7f4dab422fba5b9abfeacee14aad6db0dee",
  194. "d7a2bcbcab6a4f4af02dde8ceeef5b6ab0fd6ca5",
  195. "0b1bf0ebfc1cca19eeb029cfb8e4abd8e72d82df",
  196. "a1ea3f771dfbae1cadbc67c803b8b07081f3fdd0",
  197. "78dfa84c70545f295b3b8fa3f8bd53d316ff682e",
  198. "ca595eebf56ad6f118be2c5ddbeadac675a588ad",
  199. "faadceaec7d0bb9cbef7e7f6cddc3a0fdb8e33b8",
  200. "0b006e37964c7b1e3cdbafd21fe8e887886cb909",
  201. "dffb3dfb72fa42ed49a69dae3838eaaf2a957ee0",
  202. "2d927ebda284a25fb2cd8fc0dd42c9b7d333db5a",
  203. "ffca0cdcbe9e33fd18a3bf692ea0f2a62d685aa4",
  204. "ac0d9a8adbe32cbe7f99fdeccf22b5bf767d06fa",
  205. "f4a2a0a2833f4cbcc2db2accd7cc4feacdeeeaed",
  206. "5f4a1e0dcbd49ed99eadf4bf837f1efafea663c1",
  207. "3e69fb30ea6761771f877d82ef8ee799eb1f56a0",
  208. "2e13cb4dec42ca80918e2bdefb5df96bf9b1eebc",
  209. "d480ed3e25cb82782bb6b3f0fffce8f7c3d9840b",
  210. "7bd34bf663bd5900f0be9accb2091e3dc4a378bc",
  211. "a9fdc3ccc7cfd3b2a73b55b957bbe73beedba2ea",
  212. "b7ca933acc3ef5c8c5bfaede6bfc4f9acc7f4f39",
  213. "248a04fea1bfc38f4a1b76ea4ce399ef0affe4cb",
  214. "8ae496b4fb06c46ae5bfcdceda5e9773ca9ce25b",
  215. "0ac468471f71e8d17c21eb9fe4d922549e13bc3a",
  216. "db082f214cdbff93cdaefebabae455f2ed994730",
  217. "df0c6eabe5ccffed0bf840f3c45a8fdf39c04f9b",
  218. "aacaadfd5bd1e9efd6fc8a9b7ad8fe93ec3bbf23",
  219. "4e9cbeaabc66d01da99cd85cbaf8db5bfaccf5c4",
  220. "28b39fd14b2c8dbeab86aea69de0b5ccdf4ff7b2",
  221. "a20ebfbef4c75e906a114eaad6a67deadb810ed9",
  222. "f8fd3b4cd75b6fc28e21289e6d4ef7c4c7f22f68",
  223. "ccf7e03c5f5c7d6fdbd9fbde2fd342ae15cbbe4e",
  224. "cdcbcfe7c6d4cee31a27f338d1ff66d3cad4bbd5",
  225. "d7c03aba693ba8de8aaea7fa9d8d46a733cd5989",
  226. "48e3f4abb26eb44eefcaf3f188cea00cce46ddc1",
  227. "c1a9c25e5e29804abbc8a0fccf1faf384fea28a5",
  228. "f9e090c6b2af0ee3bde57f4a0352f5b02e675d35",
  229. "1df2354e9eee4934c34dcbd2de0b1df9b5dcadac",
  230. "7da5ad4d8fa965ea4aa4613a9dc4eabdc3406eeb",
  231. "fe33bfb676572e7fafd8ae9ea88e0efa3c109bdd",
  232. "fefeea80b187eefce0a9c184cb76976c2fcef98a",
  233. "eea8efae44ab9eb43a5954df7fa63bb55fa1d8dc",
  234. "f559c7bbe6b6bfba56e7f362604c721c2fee6d0d",
  235. "bf1dbe006b93fb443aebbe77a2c9cfdfc3f6a6a0",
  236. "cfd00d87f7bc44ed7bdcebbf493869a495ebfef8",
  237. "7ca45a388fd9aaeb8ed7701c2af2beff9e52eebb",
  238. "ca47c2fdb73b2d39a1eab115dffa1ef7a5ffacd0",
  239. "cc0954dfddaabb996abc78c28dff83f7e3edb828",
  240. "ad4adc1195dabcfaf0f7eaac4f3f3f6b152d7fd4",
  241. "ab3e6b6ed2eb6dcd4ae4fd0db76efdecd72ecfaa",
  242. "d16ccd31d433f7a41eceb7d544081c72b4d9bf3e",
  243. "d0dbe09bb150bbd5bb4b85adc273df87350e7e6c",
  244. "492bbbcaf6edd864dd3ca0aee5d4d60b1cd4214b",
  245. "d73dbbe7a9effb828793a3adad04cecb1843ccbd",
  246. "89b12dcbfc40e30ddecd7a19e0cf1e32a29ebccb",
  247. "1cd815bd965ee0fd2e15bbe28c94688f15ebb3ad",
  248. "a7f9b3babda7abfe0316bf7aeded7df7cfcf7bdf",
  249. "9e832d5ebecc2e5aead9b5cdcdd6d2ba4dfeaee7",
  250. "43fa54761faa5f8beffe9acfd402fcd24dfede71",
  251. "dada4b08ae4aa37cff754b4ca29ebc134c54bac1",
  252. "1b708f1fad29cc62bc39ae5dda8e9124acc00d2d",
  253. },
  254. }
  255. _ = allowlist.Validate()
  256. return &allowlist
  257. }()
  258. func BenchmarkCommitAllowed(b *testing.B) {
  259. for n := 0; n < b.N; n++ {
  260. ok, _ := benchCommitAllowlist.CommitAllowed("d0dbe09bb150bbd5bb4b85adc273df87350e7e6c")
  261. assert.True(b, ok)
  262. }
  263. }
  264. func BenchmarkCommitNotAllowed(b *testing.B) {
  265. for n := 0; n < b.N; n++ {
  266. ok, _ := benchCommitAllowlist.CommitAllowed("5fe58bf0b0be1735ad27aa6053b56323a905c223")
  267. assert.False(b, ok)
  268. }
  269. }
  270. var benchRegexAllowlist = func() *Allowlist {
  271. a := Allowlist{
  272. RegexTarget: "match",
  273. Regexes: []*regexp.Regexp{
  274. // Based on patterns from `generic.go`
  275. regexp.MustCompile(`(?i)access(ibility|or)`),
  276. regexp.MustCompile(`(?i)access[_.-]?id`),
  277. regexp.MustCompile(`(?i)random[_.-]?access`),
  278. regexp.MustCompile(`(?i)api[_.-]?(id|name|version)`),
  279. regexp.MustCompile(`(?i)rapid|capital`),
  280. regexp.MustCompile(`(?i)[a-z0-9-]*?api[a-z0-9-]*?:jar:`),
  281. regexp.MustCompile(`(?i)author`),
  282. regexp.MustCompile(`(?i)X-MS-Exchange-Organization-Auth`),
  283. regexp.MustCompile(`(?i)Authentication-Results`),
  284. regexp.MustCompile(`(?i)(credentials?[_.-]?id|withCredentials)`),
  285. regexp.MustCompile(`(?i)(bucket|foreign|hot|idx|natural|primary|pub(lic)?|schema|sequence)[_.-]?key`),
  286. regexp.MustCompile(`(?i)key[_.-]?(alias|board|code|frame|id|length|mesh|name|pair|ring|selector|signature|size|stone|storetype|word|up|down|left|right)`),
  287. regexp.MustCompile(`(?i)key[_.-]?vault[_.-]?(id|name)|keyVaultToStoreSecrets`),
  288. regexp.MustCompile(`(?i)key(store|tab)[_.-]?(file|path)`),
  289. regexp.MustCompile(`(?i)issuerkeyhash`),
  290. regexp.MustCompile(`(?i)(?-i:[DdMm]onkey|[DM]ONKEY)|keying`),
  291. regexp.MustCompile(`(?i)(secret)[_.-]?(length|name|size)`),
  292. regexp.MustCompile(`(?i)UserSecretsId`),
  293. regexp.MustCompile(`(?i)(io\.jsonwebtoken[ \t]?:[ \t]?[\w-]+)`),
  294. regexp.MustCompile(`(?i)(api|credentials|token)[_.-]?(endpoint|ur[il])`),
  295. regexp.MustCompile(`(?i)public[_.-]?token`),
  296. regexp.MustCompile(`(?i)(key|token)[_.-]?file`),
  297. regexp.MustCompile(`([A-Z_]+=\n[A-Z_]+=|[a-z_]+=\n[a-z_]+=)(\n|\z)`),
  298. regexp.MustCompile(`([A-Z.]+=\n[A-Z.]+=|[a-z.]+=\n[a-z.]+=)(\n|\z)`),
  299. },
  300. }
  301. _ = a.Validate()
  302. return &a
  303. }()
  304. func BenchmarkRegexAllowed(b *testing.B) {
  305. for n := 0; n < b.N; n++ {
  306. ok := benchRegexAllowlist.RegexAllowed(`environment {
  307. CREDENTIALS_ID = "K8S_CRED"
  308. }`)
  309. assert.True(b, ok)
  310. }
  311. }
  312. func BenchmarkRegexNotAllowed(b *testing.B) {
  313. for n := 0; n < b.N; n++ {
  314. ok := benchRegexAllowlist.RegexAllowed(`"credentials" : "0afae57f3ccfd9d7f5767067bc48b30f719e271ba470488056e37ab35d4b6506"`)
  315. assert.False(b, ok)
  316. }
  317. }
  318. var benchPathAllowlist = func() *Allowlist {
  319. a := Allowlist{
  320. Paths: []*regexp.Regexp{
  321. // Copied from `base/config.go`
  322. regexp.MustCompile(`gitleaks\.toml`),
  323. regexp.MustCompile(`(?i)\.(bmp|gif|jpe?g|svg|tiff?)$`),
  324. regexp.MustCompile(`\.(eot|[ot]tf|woff2?)$`),
  325. regexp.MustCompile(`(.*?)(doc|docx|zip|xls|pdf|bin|socket|vsidx|v2|suo|wsuo|.dll|pdb|exe|gltf)$`),
  326. regexp.MustCompile(`go\.(mod|sum|work(\.sum)?)$`),
  327. regexp.MustCompile(`(^|/)vendor/modules\.txt$`),
  328. regexp.MustCompile(`(^|/)vendor/(github\.com|golang\.org/x|google\.golang\.org|gopkg\.in|istio\.io|k8s\.io|sigs\.k8s\.io)(/.*)?$`),
  329. regexp.MustCompile(`(^|/)gradlew(\.bat)?$`),
  330. regexp.MustCompile(`(^|/)gradle\.lockfile$`),
  331. regexp.MustCompile(`(^|/)mvnw(\.cmd)?$`),
  332. regexp.MustCompile(`(^|/)\.mvn/wrapper/MavenWrapperDownloader\.java$`),
  333. regexp.MustCompile(`(^|/)node_modules(/.*)?$`),
  334. regexp.MustCompile(`(^|/)(npm-shrinkwrap\.json|package-lock\.json|pnpm-lock\.yaml|yarn\.lock)$`),
  335. regexp.MustCompile(`(^|/)bower_components(/.*)?$`),
  336. regexp.MustCompile(`(^|/)(angular|bootstrap|jquery(-?ui)?|plotly|swagger-?ui)[a-zA-Z0-9.-]*(\.min)?\.js(\.map)?$`),
  337. regexp.MustCompile(`(^|/)javascript\.json$`),
  338. regexp.MustCompile(`(^|/)(Pipfile|poetry)\.lock$`),
  339. regexp.MustCompile(`(?i)/?(v?env|virtualenv)/lib(64)?(/.*)?$`),
  340. regexp.MustCompile(`(?i)(^|/)(lib(64)?/python[23](\.\d{1,2})+|python/[23](\.\d{1,2})+/lib(64)?)(/.*)?$`),
  341. regexp.MustCompile(`(?i)(^|/)[a-z0-9_.]+-[0-9.]+\.dist-info(/.+)?$`),
  342. regexp.MustCompile(`(^|/)vendor/(bundle|ruby)(/.*?)?$`),
  343. regexp.MustCompile(`\.gem$`),
  344. regexp.MustCompile(`verification-metadata\.xml`),
  345. regexp.MustCompile(`Database.refactorlog`),
  346. },
  347. }
  348. _ = a.Validate()
  349. return &a
  350. }()
  351. func BenchmarkPathAllowed(b *testing.B) {
  352. for n := 0; n < b.N; n++ {
  353. ok := benchPathAllowlist.PathAllowed(`src/main/resources/static/js/jquery-ui-1.10.4.min.js`)
  354. assert.True(b, ok)
  355. }
  356. }
  357. func BenchmarkPathNotAllowed(b *testing.B) {
  358. for n := 0; n < b.N; n++ {
  359. ok := benchPathAllowlist.PathAllowed(`azure_scale_templates/sub_modules/vpc_template/inputs.auto.tfvars.json_backup`)
  360. assert.False(b, ok)
  361. }
  362. }