generic.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. package rules
  2. import (
  3. "github.com/zricethezav/gitleaks/v8/cmd/generate/config/utils"
  4. "github.com/zricethezav/gitleaks/v8/cmd/generate/secrets"
  5. "github.com/zricethezav/gitleaks/v8/config"
  6. "github.com/zricethezav/gitleaks/v8/regexp"
  7. )
  8. func GenericCredential() *config.Rule {
  9. // define rule
  10. r := config.Rule{
  11. RuleID: "generic-api-key",
  12. Description: "Detected a Generic API Key, potentially exposing access to various services and sensitive operations.",
  13. Regex: utils.GenerateSemiGenericRegex([]string{
  14. "access",
  15. "auth",
  16. `(?-i:[Aa]pi|API)`,
  17. "credential",
  18. "creds",
  19. "key",
  20. "passw(?:or)?d",
  21. "secret",
  22. "token",
  23. }, `[\w.=-]{10,150}|[a-z0-9][a-z0-9+/]{11,}={0,3}`, true),
  24. Keywords: []string{
  25. "access",
  26. "api",
  27. "auth",
  28. "key",
  29. "credential",
  30. "creds",
  31. "passwd",
  32. "password",
  33. "secret",
  34. "token",
  35. },
  36. Entropy: 3.5,
  37. Allowlists: []config.Allowlist{
  38. {
  39. // NOTE: this is a goofy hack to get around the fact there golang's regex engine does not support positive lookaheads.
  40. // Ideally we would want to ensure the secret contains both numbers and alphabetical characters, not just alphabetical characters.
  41. Regexes: []*regexp.Regexp{
  42. regexp.MustCompile(`^[a-zA-Z_.-]+$`),
  43. },
  44. },
  45. {
  46. Description: "Allowlist for Generic API Keys",
  47. MatchCondition: config.AllowlistMatchOr,
  48. RegexTarget: "match",
  49. Regexes: []*regexp.Regexp{
  50. regexp.MustCompile(`(?i)(?:` +
  51. // Access
  52. `access(?:ibility|or)` +
  53. `|access[_.-]?id` +
  54. `|random[_.-]?access` +
  55. // API
  56. `|api[_.-]?(?:id|name|version)` + // id/name/version -> not a secret
  57. `|rapid|capital` + // common words containing "api"
  58. `|[a-z0-9-]*?api[a-z0-9-]*?:jar:` + // Maven META-INF dependencies that contain "api" in the name.
  59. // Auth
  60. `|author` +
  61. `|X-MS-Exchange-Organization-Auth` + // email header
  62. `|Authentication-Results` + // email header
  63. // Credentials
  64. `|(?:credentials?[_.-]?id|withCredentials)` + // Jenkins plugins
  65. // Key
  66. `|(?:bucket|foreign|hot|idx|natural|primary|pub(?:lic)?|schema|sequence)[_.-]?key` +
  67. `|key[_.-]?(?:alias|board|code|frame|id|length|mesh|name|pair|press(?:ed)?|ring|selector|signature|size|stone|storetype|word|up|down|left|right)` +
  68. // Azure KeyVault
  69. `|key[_.-]?vault[_.-]?(?:id|name)|keyVaultToStoreSecrets` +
  70. `|key(?:store|tab)[_.-]?(?:file|path)` +
  71. `|issuerkeyhash` + // part of ssl cert
  72. `|(?-i:[DdMm]onkey|[DM]ONKEY)|keying` + // common words containing "key"
  73. // Secret
  74. `|(?:secret)[_.-]?(?:length|name|size)` + // name of e.g. env variable
  75. `|UserSecretsId` + // https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-8.0&tabs=linux
  76. // Token
  77. `|(?:csrf)[_.-]?token` +
  78. `|(?:io\.jsonwebtoken[ \t]?:[ \t]?[\w-]+)` + // Maven library coordinats. (e.g., https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt)
  79. // General
  80. `|(?:api|credentials|token)[_.-]?(?:endpoint|ur[il])` +
  81. `|public[_.-]?token` +
  82. `|(?:key|token)[_.-]?file` +
  83. // Empty variables capturing the next line (e.g., .env files)
  84. `|(?-i:(?:[A-Z_]+=\n[A-Z_]+=|[a-z_]+=\n[a-z_]+=)(?:\n|\z))` +
  85. `|(?-i:(?:[A-Z.]+=\n[A-Z.]+=|[a-z.]+=\n[a-z.]+=)(?:\n|\z))` +
  86. `)`),
  87. },
  88. StopWords: append(DefaultStopWords,
  89. "6fe4476ee5a1832882e326b506d14126", // https://github.com/yarnpkg/berry/issues/6201
  90. ),
  91. },
  92. {
  93. RegexTarget: "line",
  94. Regexes: []*regexp.Regexp{
  95. // Docker build secrets (https://docs.docker.com/build/building/secrets/#using-build-secrets).
  96. regexp.MustCompile(`--mount=type=secret,`),
  97. },
  98. },
  99. {
  100. MatchCondition: config.AllowlistMatchAnd,
  101. RegexTarget: "line",
  102. Regexes: []*regexp.Regexp{
  103. regexp.MustCompile(`LICENSE[^=]*=\s*"[^"]+`),
  104. regexp.MustCompile(`LIC_FILES_CHKSUM[^=]*=\s*"[^"]+`),
  105. regexp.MustCompile(`SRC[^=]*=\s*"[a-zA-Z0-9]+`),
  106. },
  107. Paths: []*regexp.Regexp{
  108. regexp.MustCompile(`\.bb$`),
  109. regexp.MustCompile(`\.bbappend$`),
  110. regexp.MustCompile(`\.bbclass$`),
  111. regexp.MustCompile(`\.inc$`),
  112. },
  113. },
  114. },
  115. }
  116. // validate
  117. tps := utils.GenerateSampleSecrets("generic", "CLOJARS_34bf0e88955ff5a1c328d6a7491acc4f48e865a7b8dd4d70a70749037443") //gitleaks:allow
  118. tps = append(tps, utils.GenerateSampleSecrets("generic", "Zf3D0LXCM3EIMbgJpUNnkRtOfOueHznB")...)
  119. tps = append(tps,
  120. // Access
  121. `'access_token': 'eyJ0eXAioiJKV1slS3oASx=='`,
  122. // API
  123. `some_api_token_123 = "`+newPlausibleSecret(`[a-zA-Z0-9]{60}`)+`"`,
  124. // Auth
  125. `"user_auth": "am9obmRvZTpkMDY5NGIxYi1jMTcxLTQ4ODYt+TMyYS0wMmUwOWQ1/mIwNjc="`,
  126. // Credentials
  127. `"credentials" : "0afae57f3ccfd9d7f5767067bc48b30f719e271ba470488056e37ab35d4b6506"`,
  128. `creds = `+newPlausibleSecret(`[a-zA-Z0-9]{30}`),
  129. // Key
  130. `private-key: `+newPlausibleSecret(`[a-zA-Z0-9\-_.=]{100}`),
  131. // Password
  132. `passwd = `+newPlausibleSecret(`[a-zA-Z0-9\-_.=]{30}`),
  133. // TODO: `ID=dbuser;password=` + newPlausibleSecret(`[a-zA-Z0-9+/]{30}={0,3}`) + `;"`,
  134. // Secret
  135. `"client_secret" : "6da89121079f83b2eb6acccf8219ea982c3d79bccc3e9c6a85856480661f8fde",`,
  136. `mySecretString=`+newPlausibleSecret(`[a-zA-Z0-9]{30}`),
  137. `todo_secret_do_not_commit = `+newPlausibleSecret(`[a-zA-Z0-9]{30}`),
  138. // Token
  139. ` utils.GetEnvOrDefault("api_token", "dafa7817-e246-48f3-91a7-e87653d587b8")`,
  140. // `"env": {
  141. //"API_TOKEN": "Lj2^5O%xi214"`,
  142. )
  143. fps := []string{
  144. // Access
  145. `"accessor":"rA1wk0Y45YCufyfq",`,
  146. `report_access_id: e8e4df51-2054-49b0-ab1c-516ac95c691d`,
  147. `accessibilityYesOptionId = "0736f5ef-7e88-499a-80cc-90c85d2a5180"`,
  148. `_RandomAccessIterator>
  149. _LIBCPP_CONSTEXPR_AFTER_CXX11 `,
  150. // API
  151. `this.ultraPictureBox1.Name = "ultraPictureBox1";`,
  152. `rapidstring:marm64-uwp=fail`,
  153. `event-bus-message-api:rc0.15.0_20231217_1420-SNAPSHOT'`,
  154. `COMMUNICATION_API_VERSION=rc0.13.0_20230412_0712-SNAPSHOT`,
  155. `MantleAPI_version=9a038989604e8da62ecddbe2094b16ce1b778be1`,
  156. `[DEBUG] org.slf4j.slf4j-api:jar:1.7.8.:compile (version managed from default)`,
  157. `[DEBUG] org.neo4j.neo4j-graphdb-api:jar:3.5.12:test`,
  158. `apiUrl=apigee.corpint.com`,
  159. `X-API-Name": "NRG0-Hermes-INTERNAL-API",`,
  160. // TODO: Jetbrains IML files (requires line-level allowlist).
  161. // `<orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.directory.api:api-asn1-api:1.0.0-M20" level="projcet" />`
  162. // Auth
  163. `author = "james.fake@ymail.com",`,
  164. `X-MS-Exchange-Organization-AuthSource: sm02915.int.contoso.com`,
  165. `Authentication-Results: 5h.ca.iphmx.com`,
  166. // Credentials
  167. `withCredentials([usernamePassword(credentialsId: '29f63271-dc2f-4734-8221-5b31b5169bac', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {`,
  168. `credentialsId: 'ff083f76-7804-4ef1-80e4-fe975bb9141b'`,
  169. `jobCredentialsId: 'f4aeb6bc-2a25-458a-8111-9be9e502c0e7'`,
  170. ` "credentialId": "B9mTcFSck2LzJO2S3ols63",`,
  171. `environment {
  172. CREDENTIALS_ID = "K8S_CRED"
  173. }`,
  174. `dev.credentials.url=dev-lb1.api.f4ke.com:5215`,
  175. // Key
  176. `keyword: "Befaehigung_P2"`,
  177. `public_key = "9Cnzj4p4WGeKLs1Pt8QuKUpRKfFLfRYC9AIKjbJTWit"`,
  178. `pub const X509_pubkey_st = struct_X509_pubkey_st;`,
  179. `|| pIdxKey->default_rc==0`,
  180. `monkeys-audio:mx64-uwp=fail`,
  181. `primaryKey=` + newPlausibleSecret(`[a-zA-Z0-9\-_.=]{30}`),
  182. `foreignKey=` + newPlausibleSecret(`[a-zA-Z0-9\-_.=]{30}`),
  183. `key_down_event=` + newPlausibleSecret(`[a-zA-Z0-9\-_.=]{30}`),
  184. `issuerKeyHash=` + newPlausibleSecret(`[a-zA-Z0-9\-_.=]{30}`),
  185. `<entry key="jetbrains.mps.v8_elimination" value="executed" />`,
  186. `minisat-master-keying:x64-uwp=fail`,
  187. `IceSSL.KeyFile=s_rsa1024_priv.pem`,
  188. `"bucket_key": "SalesResults-1.2"`,
  189. `<key tag="SecurityIdentifier" name="SecurityIdentifier" type="STRING" />`,
  190. // `packageKey":` + newPlausibleSecret(`[a-zA-Z0-9\-_.=]{30}`),
  191. `schemaKey = 'DOC_Vector_5_32'`,
  192. `sequenceKey = "18"`,
  193. `app.keystore.file=env/cert.p12`,
  194. `-DKEYTAB_FILE=/tmp/app.keytab`,
  195. ` doc.Security.KeySize = PdfEncryptionKeySize.Key128Bit;`,
  196. `o.keySelector=n,o.haKey=!1,`,
  197. // TODO: Requires line-level allowlists.
  198. ` "key_name": "prod5zyxlmy-cmk",`,
  199. ` "kms_key_id": "555ea4a3-d53a-4412-9c66-3a7cb667b0d6",`,
  200. ` "key_vault_name": "web21prqodx24021",`,
  201. ` keyVaultToStoreSecrets: cmp2-qat-1208358310`, // e.g., https://github.com/2uasimojo/community-operators-prod/blob/9e51e4c8e0b5caaa3087e8e18e6fb918b2c36643/operators/azure-service-operator/1.0.59040/manifests/azure.microsoft.com_cosmosdbs.yaml#L50
  202. `,apiKey:"6fe4476ee5a1832882e326b506d14126",`,
  203. `const validKeyChars = "0123456789abcdefghijklmnopqrstuvwxyz_-."`,
  204. `const keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"`,
  205. `key_length = XSalsa20.key_length`,
  206. `pub const SN_id_Gost28147_89_None_KeyMeshing = "id-Gost28147-89-None-KeyMeshing"`,
  207. `KeyPair = X25519.KeyPair`,
  208. `BlindKeySignatures = Ed25519.BlindKeySignatures`,
  209. `AVEncVideoMaxKeyframeDistance, "2987123a-ba93-4704-b489-ec1e5f25292c"`,
  210. ` keyPressed = kVK_Return.u16`,
  211. // `<add key="SchemaTable" value="G:\SchemaTable.xml" />`,
  212. //` { key: '9df21e95-3848-409d-8f94-c675cdfee839', value: 'Americas' },`,
  213. // `<TAR key="REF_ID_923.properties" value="/opts/config/alias/"/>`,
  214. // `secret:
  215. // secretName: app-decryption-secret
  216. // items:
  217. // - key: app-k8s.yml
  218. // path: app-k8s.yml`,
  219. // TODO: https://learn.microsoft.com/en-us/windows/apps/design/style/xaml-theme-resources
  220. //`<Color x:Key="NormalBrushGradient1">#FFBAE4FF</Color>`,
  221. // Password
  222. `password combination.
  223. R5: Regulatory--21`,
  224. `PuttyPassword=0`,
  225. // Secret
  226. `LLM_SECRET_NAME = "NEXUS-GPT4-API-KEY"`,
  227. ` <UserSecretsId>79a3edd0-2092-40a2-a04d-dcb46d5ca9ed</UserSecretsId>`,
  228. `secret_length = X25519.secret_length`,
  229. `secretSize must be >= XXH3_SECRET_SIZE_MIN`,
  230. `# get build time secret for authentication
  231. #RUN --mount=type=secret,id=jfrog_secret \
  232. # JFROG_SECRET = $(cat /run/secrets/jfrog_secret) && \`,
  233. // Token
  234. ` access_token_url='https://github.com/login/oauth/access_token',`,
  235. `publicToken = "9Cnzj4p4WGeKLs1Pt8QuKUpRKfFLfRYC9AIKjbJTWit"`,
  236. `<SourceFile SourceLocation="F:\Extracts\" TokenFile="RTL_INST_CODE.cer">`,
  237. `notes = "Maven - io.jsonwebtoken:jjwt-jackson-0.11.2"`,
  238. `csrf-token=Mj2qykJO5rELyHgezQ69nzUX0i3OH67V7+V4eUrLfpuyOuxmiW9rhROG/Whikle15syazJOkrjJa3U2AbhIvUw==`,
  239. // TODO: `TOKEN_AUDIENCE = "25872395-ed3a-4703-b647-22ec53f3683c"`,
  240. // General
  241. `clientId = "73082700-1f09-405b-80d0-3131bfd6272d"`,
  242. `GITHUB_API_KEY=
  243. DYNATRACE_API_KEY=`,
  244. `snowflake.password=
  245. jdbc.snowflake.url=`,
  246. // Yocto/BitBake
  247. `SRCREV_moby = "43fc912ef59a83054ea7f6706df4d53a7dea4d80"`,
  248. `LIC_FILES_CHKSUM = "file://${WORKDIR}/license.html;md5=5c94767cedb5d6987c902ac850ded2c6"`,
  249. }
  250. return utils.Validate(r, tps, fps)
  251. }
  252. func newPlausibleSecret(regex string) string {
  253. allowList := config.Allowlist{StopWords: DefaultStopWords}
  254. // attempt to generate a random secret,
  255. // retrying until it contains at least one digit and no stop words
  256. // TODO: currently the DefaultStopWords list contains many short words,
  257. // so there is a significant chance of generating a secret that contains a stop word
  258. for {
  259. secret := secrets.NewSecret(regex)
  260. if !regexp.MustCompile(`[1-9]`).MatchString(secret) {
  261. continue
  262. }
  263. if ok, _ := allowList.ContainsStopWord(secret); ok {
  264. continue
  265. }
  266. return secret
  267. }
  268. }