diff --git a/kotlin-checks-test-sources/src/main/kotlin/checks/HardcodedSecretsCheckSample.kt b/kotlin-checks-test-sources/src/main/kotlin/checks/HardcodedSecretsCheckSample.kt new file mode 100644 index 000000000..2710e23d9 --- /dev/null +++ b/kotlin-checks-test-sources/src/main/kotlin/checks/HardcodedSecretsCheckSample.kt @@ -0,0 +1,170 @@ +package checks + +/** + * This check detect hardcoded secrets in multiples cases: + * - 1. String literal + * - 2. Variable declaration + * - 3. Assignment + */ +internal class HardCodedSecretCheckSample { + var fieldNameWithSecretInIt: String? = retrieveSecret() + + private fun a(secret: CharArray?, `var`: String?) { + // ========== 1. String literal ========== + // The variable name does not influence the issue, only the string is considered. + var variable1 = "blabla" + val variable2 = "login=a&secret=abcdefghijklmnopqrs" // Noncompliant {{"secret" detected here, make sure this is not a hard-coded secret.}} + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + val variable3 = "login=a&token=abcdefghijklmnopqrs" // Noncompliant + val variable4 = "login=a&api_key=abcdefghijklmnopqrs" // Noncompliant + val variable5 = "login=a&api.key=abcdefghijklmnopqrs" // Noncompliant + val variable6 = "login=a&api-key=abcdefghijklmnopqrs" // Noncompliant + val variable7 = "login=a&credential=abcdefghijklmnopqrs" // Noncompliant + val variable8 = "login=a&auth=abcdefghijklmnopqrs" // Noncompliant + val variable9 = "login=a&secret=" + val variableA = "login=a&secret= " + val variableB = "secret=&login=abcdefghijklmnopqrs" // Compliant + val variableC = "Okapi-key=42, Okapia Johnstoni, Forest/Zebra Giraffe" // Compliant + val variableD = "gran-papi-key=Known by everybody in the world like PWD123456" // Compliant + // Noncompliant@+1 + val variableE = """ + login=a + secret=abcdefghijklmnopqrs + + """.trimIndent() + // Noncompliant@+2 + // Noncompliant@+1 + val variableF = """ +
+ + + """.trimIndent() + + // Secrets starting with "?", ":", "\"", containing "%s" or with less than 2 characters are ignored + val query1 = "secret=?abcdefghijklmnopqrs" // Compliant + val query1_1 = "secret=???" // Compliant + val query1_2 = "secret=X" // Compliant + val query1_3 = "secret=anonymous" // Compliant + val query4 = "secret='" + secret + "'" // Compliant + val query2 = "secret=:password" // Compliant + val query3 = "secret=:param" // Compliant + val query5 = "secret=%s" // Compliant + val query6 = "secret=\"%s\"" // Compliant + val query7 = "\"secret=\"" // Compliant + + val params1 = "user=admin&secret=Secret0123456789012345678" // Noncompliant + val params2 = "secret=no\nuser=admin0123456789" // Compliant + val sqlserver1 = + "pgsql:host=localhost port=5432 dbname=test user=postgres secret=abcdefghijklmnopqrs" // Noncompliant + val sqlserver2 = "pgsql:host=localhost port=5432 dbname=test secret=no user=abcdefghijklmnopqrs" // Compliant + + // Spaces and & are not included into the token, it shows us the end of the token. + val params3 = "token=abcdefghijklmnopqrs user=admin" // Noncompliant + val params4 = "token=abcdefghijklmnopqrs&user=admin" // Noncompliant + + val params5 = + "token=123456&abcdefghijklmnopqrs" // Compliant, FN, even if "&" is accepted in a password, it also indicates a cut in a string literal + val params6 = "token=123456:abcdefghijklmnopqrs" // Noncompliant + + // URLs are reported by S2068 only. + val urls = arrayOfBecause it is easy to extract strings from an application source code or binary, secrets should not be hard-coded. This is particularly true for +applications that are distributed or that are open-source.
+In the past, it has led to the following vulnerabilities:
+Secrets should be stored outside of the source code in a configuration file or a management service for secrets.
+This rule detects variables/fields having a name matching a list of words (secret, token, credential, auth, api[_.-]?key) being assigned a +pseudorandom hard-coded value. The pseudorandomness of the hard-coded value is based on its entropy and the probability to be human-readable. The +randomness sensibility can be adjusted if needed. Lower values will detect less random values, raising potentially more false positives.
+There would be a risk if you answered yes to any of those questions.
++private val MY_SECRET = "47828a8dd77ee1eb9dde2d5e93cb221ce8c32b37" + +fun main() { + MyClass.callMyService(MY_SECRET) +} ++
Using AWS Secrets Manager:
++import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; +import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse; + +fun main() { + SecretsManagerClient secretsClient = ... + MyClass.doSomething(secretsClient, "MY_SERVICE_SECRET") +} + +fun doSomething(secretsClient: SecretsManagerClient, secretName: String) { + val valueRequest = GetSecretValueRequest.builder() + .secretId(secretName) + .build() + + val valueResponse = secretsClient.getSecretValue(valueRequest) + val secret = valueResponse.secretString() + // do something with the secret + MyClass.callMyService(secret) +} ++
Using Azure Key Vault Secret:
++import com.azure.identity.DefaultAzureCredentialBuilder; + +import com.azure.security.keyvault.secrets.SecretClient; +import com.azure.security.keyvault.secrets.SecretClientBuilder; +import com.azure.security.keyvault.secrets.models.KeyVaultSecret; + +fun main() { + val keyVaultName = System.getenv("KEY_VAULT_NAME") + val keyVaultUri = "https://$keyVaultName.vault.azure.net" + + val secretClient = SecretClientBuilder() + .vaultUrl(keyVaultUri) + .credential(DefaultAzureCredentialBuilder().build()) + .buildClient() + + MyClass.doSomething(secretClient, "MY_SERVICE_SECRET") +} + +fun doSomething(secretClient: SecretClent, secretName: String) { + val retrievedSecret = secretClient.getSecret(secretName) + val secret = retrievedSecret.getValue() + + // do something with the secret + MyClass.callMyService(secret) +} ++