diff --git a/rules/python/security/python-pymongo-empty-password-python.yml b/rules/python/security/python-pymongo-empty-password-python.yml new file mode 100644 index 00000000..db0fd45c --- /dev/null +++ b/rules/python/security/python-pymongo-empty-password-python.yml @@ -0,0 +1,88 @@ +id: python-pymongo-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 + +ast-grep-essentials: true + +utils: + pymongo.MongoClient(..., password="",...): + kind: call + all: + - has: + stopBy: neighbor + kind: attribute + regex: ^pymongo.MongoClient$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: keyword_argument + all: + - has: + stopBy: neighbor + kind: identifier + regex: ^password$ + - has: + stopBy: neighbor + kind: string + not: + has: + stopBy: end + kind: string_content + # $pymongo.MongoClient(..., password="",...): + # kind: call + # all: + # - has: + # stopBy: neighbor + # kind: identifier + # regex: ^MongoClient$ + # - has: + # stopBy: neighbor + # kind: argument_list + # has: + # stopBy: neighbor + # kind: keyword_argument + # all: + # - has: + # stopBy: neighbor + # kind: identifier + # regex: ^password$ + # - has: + # stopBy: neighbor + # kind: string + # not: + # has: + # stopBy: end + # kind: string_content + # - inside: + # stopBy: end + # follows: + # stopBy: end + # kind: import_from_statement + # pattern: from pymongo import MongoClient +rule: + kind: call + any: + - matches: pymongo.MongoClient(..., password="",...) + # - matches: $pymongo.MongoClient(..., password="",...) + not: + all: + - has: + stopBy: end + kind: ERROR + - inside: + stopBy: end + kind: ERROR diff --git a/rules/python/security/python-pymongo-hardcoded-secret-python.yml b/rules/python/security/python-pymongo-hardcoded-secret-python.yml new file mode 100644 index 00000000..4690aa06 --- /dev/null +++ b/rules/python/security/python-pymongo-hardcoded-secret-python.yml @@ -0,0 +1,85 @@ +id: python-pymongo-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 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 + +ast-grep-essentials: true + +utils: + pymongo.MongoClient(..., password="",...): + kind: call + all: + - has: + stopBy: neighbor + kind: attribute + regex: ^pymongo.MongoClient$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: keyword_argument + all: + - has: + stopBy: neighbor + kind: identifier + regex: ^password$ + - has: + stopBy: neighbor + kind: string + has: + stopBy: end + kind: string_content + # $pymongo.MongoClient(..., password="",...): + # kind: call + # all: + # - has: + # stopBy: neighbor + # kind: identifier + # regex: ^MongoClient$ + # - has: + # stopBy: neighbor + # kind: argument_list + # has: + # stopBy: neighbor + # kind: keyword_argument + # all: + # - has: + # stopBy: neighbor + # kind: identifier + # regex: ^password$ + # - has: + # stopBy: neighbor + # kind: string + # has: + # stopBy: end + # kind: string_content + # - inside: + # stopBy: end + # follows: + # stopBy: end + # kind: import_from_statement + # pattern: from pymongo import MongoClient +rule: + kind: call + any: + - matches: pymongo.MongoClient(..., password="",...) + # - matches: $pymongo.MongoClient(..., password="",...) + not: + all: + - has: + stopBy: end + kind: ERROR + - inside: + stopBy: end + kind: ERROR 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..1869d3ee --- /dev/null +++ b/rules/python/security/python-webrepl-empty-password-python.yml @@ -0,0 +1,56 @@ +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 + +ast-grep-essentials: true + +utils: + webrepl.start(..., password="",...): + kind: call + all: + - has: + stopBy: neighbor + kind: attribute + regex: ^webrepl.start$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: keyword_argument + all: + - has: + stopBy: neighbor + kind: identifier + regex: ^password$ + - has: + stopBy: neighbor + kind: string + not: + has: + stopBy: end + kind: string_content +rule: + kind: call + matches: webrepl.start(..., password="",...) + not: + all: + - has: + stopBy: end + kind: ERROR + - inside: + stopBy: end + kind: ERROR + diff --git a/rules/python/security/python-webrepl-hardcoded-secret-python.yml b/rules/python/security/python-webrepl-hardcoded-secret-python.yml new file mode 100644 index 00000000..aa04e5a5 --- /dev/null +++ b/rules/python/security/python-webrepl-hardcoded-secret-python.yml @@ -0,0 +1,54 @@ +id: python-webrepl-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 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 + +ast-grep-essentials: true + +utils: + webrepl.start(..., password="",...): + kind: call + all: + - has: + stopBy: neighbor + kind: attribute + regex: ^webrepl.start$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: keyword_argument + all: + - has: + stopBy: neighbor + kind: identifier + regex: ^password$ + - has: + stopBy: neighbor + kind: string + has: + stopBy: end + kind: string_content +rule: + kind: call + matches: webrepl.start(..., password="",...) + not: + all: + - has: + stopBy: end + kind: ERROR + - inside: + stopBy: end + kind: ERROR + diff --git a/tests/__snapshots__/python-pymongo-empty-password-python-snapshot.yml b/tests/__snapshots__/python-pymongo-empty-password-python-snapshot.yml new file mode 100644 index 00000000..e6b3959a --- /dev/null +++ b/tests/__snapshots__/python-pymongo-empty-password-python-snapshot.yml @@ -0,0 +1,29 @@ +id: python-pymongo-empty-password-python +snapshots: + ? | + pymongo.MongoClient(password="") + : labels: + - source: pymongo.MongoClient(password="") + style: primary + start: 0 + end: 32 + - source: pymongo.MongoClient + style: secondary + start: 0 + end: 19 + - source: password + style: secondary + start: 20 + end: 28 + - source: '""' + style: secondary + start: 29 + end: 31 + - source: password="" + style: secondary + start: 20 + end: 31 + - source: (password="") + style: secondary + start: 19 + end: 32 diff --git a/tests/__snapshots__/python-pymongo-hardcoded-secret-python-snapshot.yml b/tests/__snapshots__/python-pymongo-hardcoded-secret-python-snapshot.yml new file mode 100644 index 00000000..473e1741 --- /dev/null +++ b/tests/__snapshots__/python-pymongo-hardcoded-secret-python-snapshot.yml @@ -0,0 +1,33 @@ +id: python-pymongo-hardcoded-secret-python +snapshots: + ? | + pymongo.MongoClient(password="a") + : labels: + - source: pymongo.MongoClient(password="a") + style: primary + start: 0 + end: 33 + - source: pymongo.MongoClient + style: secondary + start: 0 + end: 19 + - source: password + style: secondary + start: 20 + end: 28 + - source: a + style: secondary + start: 30 + end: 31 + - source: '"a"' + style: secondary + start: 29 + end: 32 + - source: password="a" + style: secondary + start: 20 + end: 32 + - source: (password="a") + style: secondary + start: 19 + end: 33 diff --git a/tests/__snapshots__/python-requests-empty-password-python-snapshot.yml b/tests/__snapshots__/python-requests-empty-password-python-snapshot.yml index 065843a3..7d5c779e 100644 --- a/tests/__snapshots__/python-requests-empty-password-python-snapshot.yml +++ b/tests/__snapshots__/python-requests-empty-password-python-snapshot.yml @@ -18,3 +18,39 @@ snapshots: style: secondary start: 89 end: 101 + ? "requests.get('https://httpbin.org/basic-auth/user/pass', auth=requests.auth.HTTPBasicAuth('user', '')) \n" + : labels: + - source: requests.auth.HTTPBasicAuth('user', '') + style: primary + start: 62 + end: 101 + - source: requests.auth.HTTPBasicAuth + style: secondary + start: 62 + end: 89 + - source: '''''' + style: secondary + start: 98 + end: 100 + - source: ('user', '') + style: secondary + start: 89 + end: 101 + 'requests.get(''https://httpbin.org/basic-auth/user/pass'', auth=requests.auth.HTTPBasicAuth(''username'', '''')) ': + labels: + - source: requests.auth.HTTPBasicAuth('username', '') + style: primary + start: 62 + end: 105 + - source: requests.auth.HTTPBasicAuth + style: secondary + start: 62 + end: 89 + - source: '''''' + style: secondary + start: 102 + end: 104 + - source: ('username', '') + style: secondary + start: 89 + end: 105 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..c9e19b40 --- /dev/null +++ b/tests/__snapshots__/python-webrepl-empty-password-python-snapshot.yml @@ -0,0 +1,29 @@ +id: python-webrepl-empty-password-python +snapshots: + ? | + webrepl.start(password="") + : labels: + - source: webrepl.start(password="") + style: primary + start: 0 + end: 26 + - source: webrepl.start + style: secondary + start: 0 + end: 13 + - source: password + style: secondary + start: 14 + end: 22 + - source: '""' + style: secondary + start: 23 + end: 25 + - source: password="" + style: secondary + start: 14 + end: 25 + - source: (password="") + style: secondary + start: 13 + end: 26 diff --git a/tests/__snapshots__/python-webrepl-hardcoded-secret-python-snapshot.yml b/tests/__snapshots__/python-webrepl-hardcoded-secret-python-snapshot.yml new file mode 100644 index 00000000..a18db1cf --- /dev/null +++ b/tests/__snapshots__/python-webrepl-hardcoded-secret-python-snapshot.yml @@ -0,0 +1,64 @@ +id: python-webrepl-hardcoded-secret-python +snapshots: + ? | + webrepl.start(password="12345") + : labels: + - source: webrepl.start(password="12345") + style: primary + start: 0 + end: 31 + - source: webrepl.start + style: secondary + start: 0 + end: 13 + - source: password + style: secondary + start: 14 + end: 22 + - source: '12345' + style: secondary + start: 24 + end: 29 + - source: '"12345"' + style: secondary + start: 23 + end: 30 + - source: password="12345" + style: secondary + start: 14 + end: 30 + - source: (password="12345") + style: secondary + start: 13 + end: 31 + ? | + webrepl.start(password="mypassword") + : labels: + - source: webrepl.start(password="mypassword") + style: primary + start: 0 + end: 36 + - source: webrepl.start + style: secondary + start: 0 + end: 13 + - source: password + style: secondary + start: 14 + end: 22 + - source: mypassword + style: secondary + start: 24 + end: 34 + - source: '"mypassword"' + style: secondary + start: 23 + end: 35 + - source: password="mypassword" + style: secondary + start: 14 + end: 35 + - source: (password="mypassword") + style: secondary + start: 13 + end: 36 diff --git a/tests/python/python-pymongo-empty-password-python-test.yml b/tests/python/python-pymongo-empty-password-python-test.yml new file mode 100644 index 00000000..581eaf8e --- /dev/null +++ b/tests/python/python-pymongo-empty-password-python-test.yml @@ -0,0 +1,7 @@ +id: python-pymongo-empty-password-python +valid: + - | + pymongo.MongoClient(password=os.env['secret']) +invalid: + - | + pymongo.MongoClient(password="") diff --git a/tests/python/python-pymongo-hardcoded-secret-python-test.yml b/tests/python/python-pymongo-hardcoded-secret-python-test.yml new file mode 100644 index 00000000..43301bc2 --- /dev/null +++ b/tests/python/python-pymongo-hardcoded-secret-python-test.yml @@ -0,0 +1,9 @@ +id: python-pymongo-hardcoded-secret-python +valid: + - | + pymongo.MongoClient(password=os.env['secret']) + - | + pymongo.MongoClient(password=os.getenv('secret')) +invalid: + - | + pymongo.MongoClient(password="a") 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..1696ea54 --- /dev/null +++ b/tests/python/python-webrepl-empty-password-python-test.yml @@ -0,0 +1,7 @@ +id: python-webrepl-empty-password-python +valid: + - | + webrepl.start(password=SECURE_PASSWORD_CONFIG["password"]) +invalid: + - | + webrepl.start(password="") diff --git a/tests/python/python-webrepl-hardcoded-secret-python-test.yml b/tests/python/python-webrepl-hardcoded-secret-python-test.yml new file mode 100644 index 00000000..a7309bdd --- /dev/null +++ b/tests/python/python-webrepl-hardcoded-secret-python-test.yml @@ -0,0 +1,9 @@ +id: python-webrepl-hardcoded-secret-python +valid: + - | + webrepl.start(password=os.getenv('PASSWORD')) +invalid: + - | + webrepl.start(password="mypassword") + - | + webrepl.start(password="12345")