diff --git a/rules/csharp/security/stacktrace-disclosure-csharp.yml b/rules/csharp/security/stacktrace-disclosure-csharp.yml new file mode 100644 index 00000000..8c27df0d --- /dev/null +++ b/rules/csharp/security/stacktrace-disclosure-csharp.yml @@ -0,0 +1,27 @@ +id: stacktrace-disclosure-csharp +severity: warning +language: csharp +message: >- + Stacktrace information is displayed in a non-Development environment. + Accidentally disclosing sensitive stack trace information in a production + environment aids an attacker in reconnaissance and information gathering. +note: >- + [CWE-209] Generation of Error Message Containing Sensitive Information. + [REFERENCES] + - https://cwe.mitre.org/data/definitions/209.html + - https://owasp.org/Top10/A04_2021-Insecure_Design/ +utils: + $APP.UseDeveloperExceptionPage(...): + kind: expression_statement + pattern: $APP.UseDeveloperExceptionPage($$$); + inside: + stopBy: neighbor + kind: block + not: + follows: + stopBy: end + kind: invocation_expression + pattern: $ENV.IsDevelopment() +rule: + kind: expression_statement + matches: $APP.UseDeveloperExceptionPage(...) \ No newline at end of file diff --git a/rules/java/security/hardcoded-secret-in-credentials-java.yml b/rules/java/security/hardcoded-secret-in-credentials-java.yml new file mode 100644 index 00000000..f7708c74 --- /dev/null +++ b/rules/java/security/hardcoded-secret-in-credentials-java.yml @@ -0,0 +1,197 @@ +id: hardcoded-secret-in-credentials-java +language: java +severity: warning +message: >- + A secret is hard-coded in the application. Secrets stored in source + code, such as credentials, identifiers, and other types of sensitive data, + can be leaked and used by internal or external malicious actors. Use + environment variables to securely provide credentials and other secrets or + retrieve them from a secure vault or Hardware Security Module (HSM). +note: >- + [CWE-798]: Use of Hard-coded Credentials + [OWASP A07:2021]: Identification and Authentication Failures + [REFERENCES] + - https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html +utils: + match_local_variable_declaration_with_username: + kind: local_variable_declaration + all: + - has: + stopBy: end + kind: type_identifier + field: type + - has: + stopBy: end + kind: variable_declarator + field: declarator + has: + stopBy: end + kind: identifier + field: name + - has: + stopBy: end + kind: method_invocation + all: + - has: + stopBy: end + kind: identifier + field: object + regex: "^Credentials$" + - has: + stopBy: end + kind: identifier + field: name + regex: "^basic$" + - has: + stopBy: end + kind: argument_list + field: arguments + all: + - has: + stopBy: end + kind: identifier + pattern: $USERNAME + - has: + kind: string_literal + pattern: $STRING + inside: + stopBy: end + kind: method_declaration + follows: + stopBy: end + kind: field_declaration + all: + - has: + stopBy: end + kind: modifiers + - has: + stopBy: end + kind: type_identifier + field: type + - has: + stopBy: end + kind: variable_declarator + field: declarator + all: + - has: + stopBy: end + kind: identifier + field: name + pattern: $USERNAME + - has: + stopBy: end + kind: string_literal + field: value + match_local_variable_declaration_with_instance: + kind: local_variable_declaration + all: + - has: + stopBy: end + kind: type_identifier + field: type + - has: + stopBy: end + kind: variable_declarator + field: declarator + all: + - has: + stopBy: end + kind: identifier + field: name + - has: + stopBy: end + kind: method_invocation + all: + - has: + stopBy: end + kind: identifier + field: object + regex: "^Credentials$" + - has: + stopBy: end + kind: identifier + field: name + regex: "^basic$" + - has: + stopBy: end + kind: argument_list + all: + - has: + stopBy: end + kind: identifier + pattern: $USERNAME + nthChild: 1 + - has: + stopBy: end + kind: identifier + pattern: $PASS + nthChild: 2 + - not: + has: + stopBy: end + kind: identifier + nthChild: 3 + - all: + - inside: + stopBy: end + kind: method_declaration + follows: + stopBy: end + kind: field_declaration + all: + - has: + stopBy: end + kind: modifiers + - has: + stopBy: end + kind: type_identifier + - has: + stopBy: end + kind: variable_declarator + field: declarator + all: + - has: + stopBy: end + kind: identifier + field: name + pattern: $PASS + - has: + stopBy: end + kind: string_literal + field: value + pattern: $STRING + - inside: + stopBy: end + kind: method_declaration + follows: + stopBy: end + kind: field_declaration + all: + - has: + stopBy: end + kind: modifiers + - has: + stopBy: end + kind: type_identifier + - has: + stopBy: end + kind: variable_declarator + field: declarator + all: + - has: + stopBy: end + kind: identifier + field: name + pattern: $USERNAME + - has: + stopBy: end + kind: string_literal + field: value +rule: + any: + - matches: match_local_variable_declaration_with_username + - matches: match_local_variable_declaration_with_instance +constraints: + STRING: + not: + regex: ^""$ diff --git a/rules/kotlin/security/jwt-hardcode-kotlin.yml b/rules/kotlin/security/jwt-hardcode-kotlin.yml new file mode 100644 index 00000000..4843667f --- /dev/null +++ b/rules/kotlin/security/jwt-hardcode-kotlin.yml @@ -0,0 +1,321 @@ +id: jwt-hardcode-kotlin +language: kotlin +severity: warning +message: >- + A secret is hard-coded in the application. Secrets stored in source + code, such as credentials, identifiers, and other types of sensitive data, + can be leaked and used by internal or external malicious actors. It is + recommended to rotate the secret and retrieve them from a secure secret + vault or Hardware Security Module (HSM), alternatively environment + variables can be used if allowed by your company policy. +note: >- + [CWE-798]: Use of Hard-coded Credentials + [OWASP A03:2021]: Identification and Authentication Failures + [REFERENCES] + - https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures +utils: + match_call_expression_Algorithm: + kind: call_expression + all: + - has: + stopBy: neighbor + kind: navigation_expression + all: + - has: + stopBy: neighbor + kind: simple_identifier + regex: "^Algorithm$" + - has: + stopBy: end + kind: navigation_suffix + has: + stopBy: end + kind: simple_identifier + regex: ^(HMAC256$|HMAC384)$ + - has: + stopBy: end + kind: call_suffix + all: + - has: + stopBy: end + kind: value_arguments + - has: + stopBy: end + kind: value_argument + has: + stopBy: end + kind: string_literal + inside: + stopBy: end + kind: property_declaration + inside: + stopBy: end + kind: function_declaration + inside: + stopBy: end + kind: object_declaration + follows: + stopBy: end + kind: import_list + has: + stopBy: end + kind: import_header + has: + stopBy: end + kind: identifier + all: + - has: + stopBy: end + kind: simple_identifier + regex: "^com$" + - has: + stopBy: end + kind: simple_identifier + regex: "^auth0$" + - has: + stopBy: end + kind: simple_identifier + regex: "^jwt$" + - has: + stopBy: end + kind: simple_identifier + regex: "^algorithms$" + - has: + stopBy: end + kind: simple_identifier + regex: "^Algorithm$" + match_call_expression_Algorithm_without_string_literal: + kind: call_expression + all: + - has: + stopBy: neighbor + kind: navigation_expression + all: + - has: + stopBy: neighbor + kind: simple_identifier + regex: "^Algorithm$" + - has: + stopBy: end + kind: navigation_suffix + has: + stopBy: end + kind: simple_identifier + regex: "^HMAC384$" + - has: + stopBy: end + kind: call_suffix + all: + - has: + stopBy: end + kind: value_arguments + - has: + stopBy: end + kind: value_argument + has: + stopBy: end + kind: simple_identifier + pattern: $SECRET + inside: + stopBy: end + kind: property_declaration + inside: + stopBy: end + kind: function_declaration + inside: + stopBy: end + kind: object_declaration + has: + stopBy: end + kind: property_declaration + all: + - has: + stopBy: end + kind: modifiers + - has: + stopBy: end + kind: variable_declaration + has: + stopBy: end + kind: simple_identifier + pattern: $SECRET + follows: + stopBy: end + kind: import_list + any: + - has: + stopBy: end + kind: import_header + has: + stopBy: end + kind: identifier + all: + - has: + stopBy: end + kind: simple_identifier + - has: + stopBy: end + kind: simple_identifier + - has: + stopBy: end + kind: simple_identifier + - has: + stopBy: end + kind: simple_identifier + - has: + stopBy: end + kind: simple_identifier + - has: + stopBy: end + kind: import_header + has: + stopBy: end + kind: identifier + all: + - has: + stopBy: end + kind: simple_identifier + - has: + stopBy: end + kind: simple_identifier + - has: + stopBy: end + kind: simple_identifier + - has: + stopBy: end + kind: simple_identifier + - has: + stopBy: end + kind: simple_identifier + - has: + stopBy: end + kind: simple_identifier + match_call_expression_depend_import: + kind: call_expression + all: + - has: + stopBy: end + kind: simple_identifier + regex: "^HMAC512$" + - has: + stopBy: end + kind: call_suffix + all: + - has: + stopBy: end + kind: value_arguments + - has: + stopBy: end + kind: value_argument + has: + stopBy: end + kind: simple_identifier + inside: + stopBy: end + kind: property_declaration + inside: + stopBy: end + kind: function_declaration + inside: + stopBy: end + kind: object_declaration + follows: + stopBy: end + kind: import_list + has: + stopBy: end + kind: import_header + has: + stopBy: end + kind: identifier + all: + - has: + stopBy: end + kind: simple_identifier + regex: "^com$" + - has: + stopBy: end + kind: simple_identifier + regex: "^auth0$" + - has: + stopBy: end + kind: simple_identifier + regex: "^jwt$" + - has: + stopBy: end + kind: simple_identifier + regex: "^algorithms$" + - has: + stopBy: end + kind: simple_identifier + regex: "^Algorithm$" + - has: + stopBy: end + kind: simple_identifier + regex: "^HMAC512$" + match_call_expression_without_Algorithm: + kind: call_expression + all: + - has: + stopBy: end + kind: simple_identifier + regex: "^HMAC512$" + - has: + stopBy: end + kind: call_suffix + all: + - has: + stopBy: end + kind: value_arguments + - has: + stopBy: end + kind: value_argument + has: + stopBy: end + kind: string_literal + inside: + stopBy: end + kind: property_declaration + inside: + stopBy: end + kind: object_declaration + follows: + stopBy: end + kind: import_list + has: + stopBy: end + kind: import_header + has: + stopBy: end + kind: identifier + all: + - has: + stopBy: end + kind: simple_identifier + regex: "^com$" + - has: + stopBy: end + kind: simple_identifier + regex: "^auth0$" + - has: + stopBy: end + kind: simple_identifier + regex: "^jwt$" + - has: + stopBy: end + kind: simple_identifier + regex: "^algorithms$" + - has: + stopBy: end + kind: simple_identifier + regex: "^Algorithm$" + - has: + stopBy: end + kind: simple_identifier + regex: "^HMAC512$" +rule: + any: + - matches: match_call_expression_Algorithm + - matches: match_call_expression_Algorithm_without_string_literal + - matches: match_call_expression_depend_import + - matches: match_call_expression_without_Algorithm diff --git a/tests/__snapshots__/hardcoded-secret-in-credentials-java-snapshot.yml b/tests/__snapshots__/hardcoded-secret-in-credentials-java-snapshot.yml new file mode 100644 index 00000000..f37aab0c --- /dev/null +++ b/tests/__snapshots__/hardcoded-secret-in-credentials-java-snapshot.yml @@ -0,0 +1,83 @@ +id: hardcoded-secret-in-credentials-java +snapshots: + ? | + import okhttp3.Request; + import okhttp3.RequestBody; + import okhttp3.Credentials; + public class OkhttpSecretBasicAuth { + private String username = "wowee"; + private String password = "hi"; + private String empty = ""; + public void run() { + String credential = Credentials.basic(username, "asdf"); + : labels: + - source: String credential = Credentials.basic(username, "asdf"); + style: primary + start: 231 + end: 287 + - source: String + style: secondary + start: 231 + end: 237 + - source: credential + style: secondary + start: 238 + end: 248 + - source: credential = Credentials.basic(username, "asdf") + style: secondary + start: 238 + end: 286 + - source: Credentials + style: secondary + start: 251 + end: 262 + - source: basic + style: secondary + start: 263 + end: 268 + - source: username + style: secondary + start: 269 + end: 277 + - source: '"asdf"' + style: secondary + start: 279 + end: 285 + - source: (username, "asdf") + style: secondary + start: 268 + end: 286 + - source: Credentials.basic(username, "asdf") + style: secondary + start: 251 + end: 286 + - source: private + style: secondary + start: 117 + end: 124 + - source: String + style: secondary + start: 125 + end: 131 + - source: username + style: secondary + start: 132 + end: 140 + - source: '"wowee"' + style: secondary + start: 143 + end: 150 + - source: username = "wowee" + style: secondary + start: 132 + end: 150 + - source: private String username = "wowee"; + style: secondary + start: 117 + end: 151 + - source: |- + public void run() { + String credential = Credentials.basic(username, "asdf"); + style: secondary + start: 211 + end: 287 diff --git a/tests/__snapshots__/jwt-hardcode-kotlin-snapshot.yml b/tests/__snapshots__/jwt-hardcode-kotlin-snapshot.yml new file mode 100644 index 00000000..4f7a628e --- /dev/null +++ b/tests/__snapshots__/jwt-hardcode-kotlin-snapshot.yml @@ -0,0 +1,124 @@ +id: jwt-hardcode-kotlin +snapshots: + ? | + package com.foobar.org.configuration + import com.auth0.jwt.JWT + import com.auth0.jwt.algorithms.Algorithm + import com.auth0.jwt.algorithms.Algorithm.HMAC512 + import com.auth0.jwt.exceptions.JWTCreationException + object App { + private fun bad1() { + try { + val algorithm = Algorithm.HMAC256("secret") + val token = JWT.create() + .withIssuer("auth0") + .sign(algorithm) + } catch (exception: JWTCreationException) { + } + } + } + : labels: + - source: Algorithm.HMAC256("secret") + style: primary + start: 263 + end: 290 + - source: Algorithm + style: secondary + start: 263 + end: 272 + - source: HMAC256 + style: secondary + start: 273 + end: 280 + - source: .HMAC256 + style: secondary + start: 272 + end: 280 + - source: Algorithm.HMAC256 + style: secondary + start: 263 + end: 280 + - source: ("secret") + style: secondary + start: 280 + end: 290 + - source: '"secret"' + style: secondary + start: 281 + end: 289 + - source: '"secret"' + style: secondary + start: 281 + end: 289 + - source: ("secret") + style: secondary + start: 280 + end: 290 + - source: com + style: secondary + start: 69 + end: 72 + - source: auth0 + style: secondary + start: 73 + end: 78 + - source: jwt + style: secondary + start: 79 + end: 82 + - source: algorithms + style: secondary + start: 83 + end: 93 + - source: Algorithm + style: secondary + start: 94 + end: 103 + - source: com.auth0.jwt.algorithms.Algorithm + style: secondary + start: 69 + end: 103 + - source: import com.auth0.jwt.algorithms.Algorithm + style: secondary + start: 62 + end: 103 + - source: |- + import com.auth0.jwt.JWT + import com.auth0.jwt.algorithms.Algorithm + import com.auth0.jwt.algorithms.Algorithm.HMAC512 + import com.auth0.jwt.exceptions.JWTCreationException + style: secondary + start: 37 + end: 206 + - source: |- + object App { + private fun bad1() { + try { + val algorithm = Algorithm.HMAC256("secret") + val token = JWT.create() + .withIssuer("auth0") + .sign(algorithm) + } catch (exception: JWTCreationException) { + } + } + } + style: secondary + start: 207 + end: 403 + - source: |- + private fun bad1() { + try { + val algorithm = Algorithm.HMAC256("secret") + val token = JWT.create() + .withIssuer("auth0") + .sign(algorithm) + } catch (exception: JWTCreationException) { + } + } + style: secondary + start: 220 + end: 401 + - source: val algorithm = Algorithm.HMAC256("secret") + style: secondary + start: 247 + end: 290 diff --git a/tests/__snapshots__/stacktrace-disclosure-csharp-snapshot.yml b/tests/__snapshots__/stacktrace-disclosure-csharp-snapshot.yml new file mode 100644 index 00000000..0a18165a --- /dev/null +++ b/tests/__snapshots__/stacktrace-disclosure-csharp-snapshot.yml @@ -0,0 +1,72 @@ +id: stacktrace-disclosure-csharp +snapshots: + ? "if (!env.IsDevelopment()) \n {\n app.UseDeveloperExceptionPage(); \n }\n" + : labels: + - source: app.UseDeveloperExceptionPage(); + style: primary + start: 42 + end: 74 + - source: "{\n app.UseDeveloperExceptionPage(); \n }" + style: secondary + start: 32 + end: 82 + ? "if (DateTime.Now.DayOfWeek == DayOfWeek.Monday) \n {\n app.UseDeveloperExceptionPage(); \n }\n" + : labels: + - source: app.UseDeveloperExceptionPage(); + style: primary + start: 63 + end: 95 + - source: "{\n app.UseDeveloperExceptionPage(); \n }" + style: secondary + start: 53 + end: 102 + ? "if (DateTime.Now.DayOfWeek == DayOfWeek.Monday) \n {\n app.UseDeveloperExceptionPage();\n }\n" + : labels: + - source: app.UseDeveloperExceptionPage(); + style: primary + start: 64 + end: 96 + - source: |- + { + app.UseDeveloperExceptionPage(); + } + style: secondary + start: 54 + end: 102 + ? "if (env.IsProduction()) \n {\n app.UseDeveloperExceptionPage(); \n }\n" + : labels: + - source: app.UseDeveloperExceptionPage(); + style: primary + start: 40 + end: 72 + - source: "{\n app.UseDeveloperExceptionPage(); \n }" + style: secondary + start: 30 + end: 79 + ? "if (environment == \"dev\") \n {\n app.UseDeveloperExceptionPage(); \n }\n" + : labels: + - source: app.UseDeveloperExceptionPage(); + style: primary + start: 42 + end: 74 + - source: "{\n app.UseDeveloperExceptionPage(); \n }" + style: secondary + start: 32 + end: 82 + ? | + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseDeveloperExceptionPage(); + } + : labels: + - source: app.UseDeveloperExceptionPage(); + style: primary + start: 74 + end: 106 + - source: |- + { + app.UseDeveloperExceptionPage(); + } + style: secondary + start: 72 + end: 108 diff --git a/tests/csharp/stacktrace-disclosure-csharp-test.yml b/tests/csharp/stacktrace-disclosure-csharp-test.yml new file mode 100644 index 00000000..2c5a23ac --- /dev/null +++ b/tests/csharp/stacktrace-disclosure-csharp-test.yml @@ -0,0 +1,39 @@ +id: stacktrace-disclosure-csharp +valid: + - | + if (env.IsDevelopment()) + { + app.UseExceptionHandler("/Error"); + } +invalid: + - | + if (env.IsProduction()) + { + app.UseDeveloperExceptionPage(); + } + - | + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseDeveloperExceptionPage(); + } + - | + if (!env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + - | + if (DateTime.Now.DayOfWeek == DayOfWeek.Monday) + { + app.UseDeveloperExceptionPage(); + } + - | + if (DateTime.Now.DayOfWeek == DayOfWeek.Monday) + { + app.UseDeveloperExceptionPage(); + } + - | + if (environment == "dev") + { + app.UseDeveloperExceptionPage(); + } + diff --git a/tests/java/hardcoded-secret-in-credentials-java-test.yml b/tests/java/hardcoded-secret-in-credentials-java-test.yml new file mode 100644 index 00000000..450bbbd2 --- /dev/null +++ b/tests/java/hardcoded-secret-in-credentials-java-test.yml @@ -0,0 +1,15 @@ +id: hardcoded-secret-in-credentials-java +valid: + - | + String credential = Credentials.basic(username, System.getenv("PASSWORD")); +invalid: + - | + import okhttp3.Request; + import okhttp3.RequestBody; + import okhttp3.Credentials; + public class OkhttpSecretBasicAuth { + private String username = "wowee"; + private String password = "hi"; + private String empty = ""; + public void run() { + String credential = Credentials.basic(username, "asdf"); diff --git a/tests/kotlin/jwt-hardcode-kotlin-test.yml b/tests/kotlin/jwt-hardcode-kotlin-test.yml new file mode 100644 index 00000000..9eb858c6 --- /dev/null +++ b/tests/kotlin/jwt-hardcode-kotlin-test.yml @@ -0,0 +1,23 @@ +id: jwt-hardcode-kotlin +valid: + - | + System.setProperty("javax.net.ssl.trustStorePassword", config); + System.setProperty("javax.net.ssl.keyStorePassword", config); +invalid: + - | + package com.foobar.org.configuration + import com.auth0.jwt.JWT + import com.auth0.jwt.algorithms.Algorithm + import com.auth0.jwt.algorithms.Algorithm.HMAC512 + import com.auth0.jwt.exceptions.JWTCreationException + object App { + private fun bad1() { + try { + val algorithm = Algorithm.HMAC256("secret") + val token = JWT.create() + .withIssuer("auth0") + .sign(algorithm) + } catch (exception: JWTCreationException) { + } + } + }