diff --git a/rules/python/security/python-tormysql-empty-password-python.yml b/rules/python/security/python-tormysql-empty-password-python.yml new file mode 100644 index 00000000..963bbdfa --- /dev/null +++ b/rules/python/security/python-tormysql-empty-password-python.yml @@ -0,0 +1,206 @@ +id: python-tormysql-empty-password-python +language: python +severity: warning +message: >- + The application creates a database connection with an empty password. + This can lead to unauthorized access by either an internal or external + malicious actor. To prevent this vulnerability, enforce authentication + when connecting to a database by using environment variables to securely + provide credentials or retrieving them from a secure vault or HSM + (Hardware Security Module). +note: >- + [CWE-287]: Improper Authentication + [OWASP A07:2021]: Identification and Authentication Failures + [REFERENCES] + https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html +utils: + match_expression_statement: + kind: expression_statement + has: + stopBy: end + kind: call + all: + - has: + stopBy: end + kind: attribute + field: function + all: + - has: + stopBy: end + kind: identifier + field: object + regex: "^tormysql$" + - has: + stopBy: end + kind: identifier + field: attribute + regex: "^ConnectionPool$" + - has: + stopBy: end + kind: argument_list + field: arguments + has: + stopBy: end + kind: keyword_argument + all: + - has: + stopBy: end + kind: identifier + field: name + regex: "^password$" + - has: + kind: string + field: value + all: + - has: + kind: string_start + not: + precedes: + stopBy: end + kind: string_content + - has: + kind: string_end + + match_expression_statement_with_identifier: + kind: expression_statement + has: + stopBy: end + kind: call + all: + - has: + stopBy: end + kind: attribute + field: function + all: + - has: + stopBy: end + kind: identifier + field: object + regex: "^tormysql$" + - has: + stopBy: end + kind: identifier + field: attribute + regex: "^ConnectionPool$" + - has: + stopBy: end + kind: argument_list + field: arguments + has: + stopBy: end + kind: keyword_argument + all: + - has: + kind: identifier + field: name + regex: "^password$" + - has: + kind: identifier + field: value + pattern: $PASS + follows: + stopBy: end + kind: expression_statement + has: + stopBy: end + kind: assignment + all: + - has: + stopBy: end + kind: identifier + pattern: $PASS + - has: + stopBy: end + kind: string + match_direct_expression_with_passwd: + kind: call + all: + - has: + kind: attribute + field: function + all: + - has: + kind: identifier + field: object + regex: "^tormysql$" + - has: + kind: identifier + field: attribute + regex: "^ConnectionPool$" + - has: + kind: argument_list + field: arguments + has: + kind: keyword_argument + all: + - has: + kind: identifier + field: name + regex: ^(passwd|password)$ + - has: + kind: string + field: value + all: + - has: + kind: string_start + - has: + kind: string_end + inside: + stopBy: end + kind: return_statement + inside: + stopBy: end + kind: function_definition + has: + kind: identifier + field: name + match_direct_expression_with_passwd_without_return: + kind: call + all: + - has: + stopBy: end + kind: attribute + field: function + all: + - has: + stopBy: end + kind: identifier + field: object + regex: "^tormysql$" + - has: + stopBy: end + kind: identifier + field: attribute + regex: "^ConnectionPool$" + - has: + stopBy: end + kind: argument_list + field: arguments + has: + stopBy: end + kind: keyword_argument + all: + - has: + kind: identifier + field: name + regex: ^(passwd)$ + - has: + kind: string + all: + - has: + kind: string_start + - has: + kind: string_end + inside: + stopBy: end + kind: assignment + inside: + stopBy: end + kind: expression_statement + +rule: + any: + - matches: match_expression_statement + - matches: match_expression_statement_with_identifier + - matches: match_direct_expression_with_passwd + - matches: match_direct_expression_with_passwd_without_return diff --git a/rules/python/security/python-tormysql-hardcoded-secret-python.yml b/rules/python/security/python-tormysql-hardcoded-secret-python.yml new file mode 100644 index 00000000..5c7ccb6d --- /dev/null +++ b/rules/python/security/python-tormysql-hardcoded-secret-python.yml @@ -0,0 +1,159 @@ +id: python-tormysql-hardcoded-secret-python +language: python +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 crede ntials 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_expression_statement: + kind: expression_statement + has: + stopBy: end + kind: call + all: + - has: + stopBy: end + kind: attribute + field: function + all: + - has: + stopBy: end + kind: identifier + field: object + regex: "^tormysql$" + - has: + stopBy: end + kind: identifier + field: attribute + regex: "^ConnectionPool$" + - has: + stopBy: end + kind: argument_list + field: arguments + has: + stopBy: end + kind: keyword_argument + all: + - has: + stopBy: end + kind: identifier + field: name + regex: ^(password|passwd)$ + - has: + kind: string + all: + - has: + kind: string_start + - has: + kind: string_content + - has: + kind: string_end + match_expression_statement_with_identifier: + kind: expression_statement + has: + stopBy: end + kind: call + all: + - has: + stopBy: end + kind: attribute + field: function + all: + - has: + stopBy: end + kind: identifier + field: object + regex: "^tormysql$" + - has: + stopBy: end + kind: identifier + field: attribute + regex: "^ConnectionPool$" + - has: + stopBy: end + kind: argument_list + field: arguments + has: + stopBy: end + kind: keyword_argument + all: + - has: + stopBy: end + kind: identifier + field: name + regex: "^password$" + - has: + stopBy: end + kind: identifier + field: value + pattern: $PASS + follows: + stopBy: end + kind: expression_statement + has: + stopBy: end + kind: assignment + all: + - has: + kind: identifier + pattern: $PASS + - has: + kind: string + all: + - has: + kind: string_start + - has: + kind: string_content + - has: + kind: string_end + match_call_with_return: + kind: call + all: + - has: + kind: attribute + field: function + all: + - has: + kind: identifier + field: object + regex: "^tormysql$" + - has: + kind: identifier + field: attribute + regex: "^ConnectionPool$" + - has: + kind: argument_list + field: arguments + has: + kind: keyword_argument + all: + - has: + kind: identifier + field: name + regex: "^password$" + - has: + kind: string + field: value + all: + - has: + kind: string_start + - has: + kind: string_content + - has: + kind: string_end + inside: + stopBy: end + kind: return_statement +rule: + any: + - matches: match_expression_statement + - matches: match_expression_statement_with_identifier + - matches: match_call_with_return diff --git a/rules/python/security/python-webrepl-empty-password-python.yml b/rules/python/security/python-webrepl-empty-password-python.yml new file mode 100644 index 00000000..df115692 --- /dev/null +++ b/rules/python/security/python-webrepl-empty-password-python.yml @@ -0,0 +1,117 @@ +id: python-webrepl-empty-password-python +language: python +severity: warning +message: >- + The application creates a database connection with an empty password. + This can lead to unauthorized access by either an internal or external + malicious actor. To prevent this vulnerability, enforce authentication + when connecting to a database by using environment variables to securely + provide credentials or retrieving them from a secure vault or HSM + (Hardware Security Module). +note: >- + [CWE-287]: Improper Authentication + [OWASP A07:2021]: Identification and Authentication Failures + [REFERENCES] + https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html +utils: + match_call: + kind: call + all: + - has: + kind: attribute + all: + - has: + kind: identifier + field: object + regex: "^webrepl$" + - has: + kind: identifier + field: attribute + regex: "^start$" + - has: + kind: argument_list + has: + kind: keyword_argument + all: + - has: + kind: identifier + field: name + regex: "^password$" + - has: + kind: string + field: value + all: + - has: + kind: string_start + not: + precedes: + stopBy: end + kind: string_content + - has: + kind: string_end + inside: + stopBy: end + kind: expression_statement + match_call_with_identifier: + kind: call + all: + - has: + stopBy: end + kind: attribute + field: function + all: + - has: + stopBy: end + kind: identifier + field: object + regex: "^webrepl$" + - has: + stopBy: end + kind: identifier + field: attribute + regex: "^start$" + - has: + stopBy: end + kind: argument_list + field: arguments + has: + stopBy: end + kind: keyword_argument + all: + - has: + stopBy: end + kind: identifier + field: name + regex: "^password$" + - has: + stopBy: end + kind: identifier + field: value + pattern: $PASS + inside: + stopBy: end + kind: expression_statement + follows: + stopBy: end + kind: expression_statement + has: + stopBy: end + kind: assignment + all: + - has: + kind: identifier + pattern: $PASS + - has: + kind: string + all: + - has: + kind: string_start + not: + precedes: + kind: string_content + - has: + kind: string_end +rule: + any: + - matches: match_call + - matches: match_call_with_identifier diff --git a/tests/__snapshots__/python-tormysql-empty-password-python-snapshot.yml b/tests/__snapshots__/python-tormysql-empty-password-python-snapshot.yml new file mode 100644 index 00000000..bb770b2a --- /dev/null +++ b/tests/__snapshots__/python-tormysql-empty-password-python-snapshot.yml @@ -0,0 +1,156 @@ +id: python-tormysql-empty-password-python +snapshots: + ? | + EMPTY_PASSWORD = "" + conn2 = tormysql.ConnectionPool(password=EMPTY_PASSWORD) + : labels: + - source: conn2 = tormysql.ConnectionPool(password=EMPTY_PASSWORD) + style: primary + start: 20 + end: 76 + - source: tormysql + style: secondary + start: 28 + end: 36 + - source: ConnectionPool + style: secondary + start: 37 + end: 51 + - source: tormysql.ConnectionPool + style: secondary + start: 28 + end: 51 + - source: password + style: secondary + start: 52 + end: 60 + - source: EMPTY_PASSWORD + style: secondary + start: 61 + end: 75 + - source: password=EMPTY_PASSWORD + style: secondary + start: 52 + end: 75 + - source: (password=EMPTY_PASSWORD) + style: secondary + start: 51 + end: 76 + - source: tormysql.ConnectionPool(password=EMPTY_PASSWORD) + style: secondary + start: 28 + end: 76 + - source: EMPTY_PASSWORD + style: secondary + start: 0 + end: 14 + - source: '""' + style: secondary + start: 17 + end: 19 + - source: EMPTY_PASSWORD = "" + style: secondary + start: 0 + end: 19 + - source: EMPTY_PASSWORD = "" + style: secondary + start: 0 + end: 19 + ? | + conn1 = tormysql.ConnectionPool(password="") + : labels: + - source: conn1 = tormysql.ConnectionPool(password="") + style: primary + start: 0 + end: 44 + - source: tormysql + style: secondary + start: 8 + end: 16 + - source: ConnectionPool + style: secondary + start: 17 + end: 31 + - source: tormysql.ConnectionPool + style: secondary + start: 8 + end: 31 + - source: password + style: secondary + start: 32 + end: 40 + - source: '"' + style: secondary + start: 41 + end: 42 + - source: '"' + style: secondary + start: 42 + end: 43 + - source: '""' + style: secondary + start: 41 + end: 43 + - source: password="" + style: secondary + start: 32 + end: 43 + - source: (password="") + style: secondary + start: 31 + end: 44 + - source: tormysql.ConnectionPool(password="") + style: secondary + start: 8 + end: 44 + ? | + conn4 = tormysql.ConnectionPool(passwd="") + : labels: + - source: tormysql.ConnectionPool(passwd="") + style: primary + start: 8 + end: 42 + - source: tormysql + style: secondary + start: 8 + end: 16 + - source: ConnectionPool + style: secondary + start: 17 + end: 31 + - source: tormysql.ConnectionPool + style: secondary + start: 8 + end: 31 + - source: passwd + style: secondary + start: 32 + end: 38 + - source: '"' + style: secondary + start: 39 + end: 40 + - source: '"' + style: secondary + start: 40 + end: 41 + - source: '""' + style: secondary + start: 39 + end: 41 + - source: passwd="" + style: secondary + start: 32 + end: 41 + - source: (passwd="") + style: secondary + start: 31 + end: 42 + - source: conn4 = tormysql.ConnectionPool(passwd="") + style: secondary + start: 0 + end: 42 + - source: conn4 = tormysql.ConnectionPool(passwd="") + style: secondary + start: 0 + end: 42 diff --git a/tests/__snapshots__/python-tormysql-hardcoded-secret-python-snapshot.yml b/tests/__snapshots__/python-tormysql-hardcoded-secret-python-snapshot.yml new file mode 100644 index 00000000..8d2a098f --- /dev/null +++ b/tests/__snapshots__/python-tormysql-hardcoded-secret-python-snapshot.yml @@ -0,0 +1,121 @@ +id: python-tormysql-hardcoded-secret-python +snapshots: + ? | + HARDCODED_PASSWORD = "123secure" + conn4 = tormysql.ConnectionPool(password=HARDCODED_PASSWORD) + : labels: + - source: conn4 = tormysql.ConnectionPool(password=HARDCODED_PASSWORD) + style: primary + start: 33 + end: 93 + - source: tormysql + style: secondary + start: 41 + end: 49 + - source: ConnectionPool + style: secondary + start: 50 + end: 64 + - source: tormysql.ConnectionPool + style: secondary + start: 41 + end: 64 + - source: password + style: secondary + start: 65 + end: 73 + - source: HARDCODED_PASSWORD + style: secondary + start: 74 + end: 92 + - source: password=HARDCODED_PASSWORD + style: secondary + start: 65 + end: 92 + - source: (password=HARDCODED_PASSWORD) + style: secondary + start: 64 + end: 93 + - source: tormysql.ConnectionPool(password=HARDCODED_PASSWORD) + style: secondary + start: 41 + end: 93 + - source: HARDCODED_PASSWORD + style: secondary + start: 0 + end: 18 + - source: '"' + style: secondary + start: 21 + end: 22 + - source: 123secure + style: secondary + start: 22 + end: 31 + - source: '"' + style: secondary + start: 31 + end: 32 + - source: '"123secure"' + style: secondary + start: 21 + end: 32 + - source: HARDCODED_PASSWORD = "123secure" + style: secondary + start: 0 + end: 32 + - source: HARDCODED_PASSWORD = "123secure" + style: secondary + start: 0 + end: 32 + ? | + conn1 = tormysql.ConnectionPool(password="hardcoded_password") + : labels: + - source: conn1 = tormysql.ConnectionPool(password="hardcoded_password") + style: primary + start: 0 + end: 62 + - source: tormysql + style: secondary + start: 8 + end: 16 + - source: ConnectionPool + style: secondary + start: 17 + end: 31 + - source: tormysql.ConnectionPool + style: secondary + start: 8 + end: 31 + - source: password + style: secondary + start: 32 + end: 40 + - source: '"' + style: secondary + start: 41 + end: 42 + - source: hardcoded_password + style: secondary + start: 42 + end: 60 + - source: '"' + style: secondary + start: 60 + end: 61 + - source: '"hardcoded_password"' + style: secondary + start: 41 + end: 61 + - source: password="hardcoded_password" + style: secondary + start: 32 + end: 61 + - source: (password="hardcoded_password") + style: secondary + start: 31 + end: 62 + - source: tormysql.ConnectionPool(password="hardcoded_password") + style: secondary + start: 8 + end: 62 diff --git a/tests/__snapshots__/python-webrepl-empty-password-python-snapshot.yml b/tests/__snapshots__/python-webrepl-empty-password-python-snapshot.yml new file mode 100644 index 00000000..d51c3828 --- /dev/null +++ b/tests/__snapshots__/python-webrepl-empty-password-python-snapshot.yml @@ -0,0 +1,113 @@ +id: python-webrepl-empty-password-python +snapshots: + ? | + EMPTY_PASSWORD = "" + webrepl.start(password=EMPTY_PASSWORD) + : labels: + - source: webrepl.start(password=EMPTY_PASSWORD) + style: primary + start: 20 + end: 58 + - source: webrepl + style: secondary + start: 20 + end: 27 + - source: start + style: secondary + start: 28 + end: 33 + - source: webrepl.start + style: secondary + start: 20 + end: 33 + - source: password + style: secondary + start: 34 + end: 42 + - source: EMPTY_PASSWORD + style: secondary + start: 43 + end: 57 + - source: password=EMPTY_PASSWORD + style: secondary + start: 34 + end: 57 + - source: (password=EMPTY_PASSWORD) + style: secondary + start: 33 + end: 58 + - source: EMPTY_PASSWORD + style: secondary + start: 0 + end: 14 + - source: '"' + style: secondary + start: 17 + end: 18 + - source: '"' + style: secondary + start: 18 + end: 19 + - source: '""' + style: secondary + start: 17 + end: 19 + - source: EMPTY_PASSWORD = "" + style: secondary + start: 0 + end: 19 + - source: EMPTY_PASSWORD = "" + style: secondary + start: 0 + end: 19 + - source: webrepl.start(password=EMPTY_PASSWORD) + style: secondary + start: 20 + end: 58 + ? | + webrepl.start(password="") + : labels: + - source: webrepl.start(password="") + style: primary + start: 0 + end: 26 + - source: webrepl + style: secondary + start: 0 + end: 7 + - source: start + style: secondary + start: 8 + end: 13 + - source: webrepl.start + style: secondary + start: 0 + end: 13 + - source: password + style: secondary + start: 14 + end: 22 + - source: '"' + style: secondary + start: 23 + end: 24 + - source: '"' + style: secondary + start: 24 + end: 25 + - source: '""' + style: secondary + start: 23 + end: 25 + - source: password="" + style: secondary + start: 14 + end: 25 + - source: (password="") + style: secondary + start: 13 + end: 26 + - source: webrepl.start(password="") + style: secondary + start: 0 + end: 26 diff --git a/tests/python/python-tormysql-empty-password-python-test.yml b/tests/python/python-tormysql-empty-password-python-test.yml new file mode 100644 index 00000000..a5c76c49 --- /dev/null +++ b/tests/python/python-tormysql-empty-password-python-test.yml @@ -0,0 +1,12 @@ +id: python-tormysql-empty-password-python +valid: + - | + conn7 = tormysql.ConnectionPool(password=CONFIG["db_password"]) +invalid: + - | + conn1 = tormysql.ConnectionPool(password="") + - | + EMPTY_PASSWORD = "" + conn2 = tormysql.ConnectionPool(password=EMPTY_PASSWORD) + - | + conn4 = tormysql.ConnectionPool(passwd="") diff --git a/tests/python/python-tormysql-hardcoded-secret-python-test.yml b/tests/python/python-tormysql-hardcoded-secret-python-test.yml new file mode 100644 index 00000000..f8bb028c --- /dev/null +++ b/tests/python/python-tormysql-hardcoded-secret-python-test.yml @@ -0,0 +1,13 @@ +id: python-tormysql-hardcoded-secret-python +valid: + - | + conn9 = tormysql.ConnectionPool(passwd="") + - | + SECURE_CONFIG = {"password": os.getenv("SECURE_DB_PASSWORD")} + conn11 = tormysql.ConnectionPool(password=SECURE_CONFIG["password"]) +invalid: + - | + conn1 = tormysql.ConnectionPool(password="hardcoded_password") + - | + HARDCODED_PASSWORD = "123secure" + conn4 = tormysql.ConnectionPool(password=HARDCODED_PASSWORD) diff --git a/tests/python/python-webrepl-empty-password-python-test.yml b/tests/python/python-webrepl-empty-password-python-test.yml new file mode 100644 index 00000000..9f4dae8b --- /dev/null +++ b/tests/python/python-webrepl-empty-password-python-test.yml @@ -0,0 +1,11 @@ +id: python-webrepl-empty-password-python +valid: + - | + SECURE_PASSWORD_CONFIG = {"password": os.getenv("SECURE_PASSWORD")} + webrepl.start(password=SECURE_PASSWORD_CONFIG["password"]) +invalid: + - | + webrepl.start(password="") + - | + EMPTY_PASSWORD = "" + webrepl.start(password=EMPTY_PASSWORD)