From b0b432e74b9591039527de8f17793be7e10fc6ba Mon Sep 17 00:00:00 2001 From: Sakshis Date: Wed, 5 Mar 2025 11:00:04 +0000 Subject: [PATCH 1/4] hashids-with-django-secret-python --- .../hashids-with-django-secret-python.yml | 285 ++++++++++++++++++ ...ids-with-django-secret-python-snapshot.yml | 252 ++++++++++++++++ ...by-mysql2-empty-password-ruby-snapshot.yml | 66 ++++ ...hashids-with-django-secret-python-test.yml | 33 ++ 4 files changed, 636 insertions(+) create mode 100644 rules/python/security/hashids-with-django-secret-python.yml create mode 100644 tests/__snapshots__/hashids-with-django-secret-python-snapshot.yml create mode 100644 tests/python/hashids-with-django-secret-python-test.yml diff --git a/rules/python/security/hashids-with-django-secret-python.yml b/rules/python/security/hashids-with-django-secret-python.yml new file mode 100644 index 00000000..94104dfe --- /dev/null +++ b/rules/python/security/hashids-with-django-secret-python.yml @@ -0,0 +1,285 @@ +id: hashids-with-django-secret-python +language: python +severity: warning +message: >- + The Django secret key is used as salt in HashIDs. The HashID mechanism + is not secure. By observing sufficient HashIDs, the salt used to construct + them can be recovered. This means the Django secret key can be obtained by + attackers, through the HashIDs. +note: >- + [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm + [OWASP A02:2021]: Cryptographic Failures + [REFERENCES] + https://docs.djangoproject.com/en/4.2/ref/settings/#std-setting-SECRET_KEY + http://carnage.github.io/2015/08/cryptanalysis-of-hashids +ast-grep-essentials: true +utils: + Hashids(salt=settings.SECRET_KEY, min_length=settings.ID_HASH_MIN_LENGTH): + kind: call + all: + - has: + kind: identifier + nthChild: 1 + regex: ^Hashids$ + - has: + kind: argument_list + nthChild: 2 + has: + kind: keyword_argument + all: + - has: + kind: identifier + regex: ^salt$ + - has: + kind: attribute + all: + - has: + kind: identifier + regex: ^settings$ + nthChild: 1 + - has: + kind: identifier + nthChild: 2 + regex: ^SECRET_KEY$ + - all: + - any: + - inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + pattern: from django.conf import settings + - follows: + stopBy: end + kind: import_from_statement + pattern: from django.conf import settings + - any: + - inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + pattern: from hashids import Hashids + - follows: + stopBy: end + kind: import_from_statement + pattern: from hashids import Hashids + + Hashids(settings.SECRET_KEY, min_length=length, alphabet=alphabet): + kind: call + all: + - has: + kind: identifier + nthChild: 1 + regex: ^Hashids$ + - has: + kind: argument_list + nthChild: 2 + has: + kind: attribute + all: + - has: + kind: identifier + regex: ^settings$ + nthChild: 1 + - has: + kind: identifier + nthChild: 2 + regex: ^SECRET_KEY$ + - all: + - any: + - inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + pattern: from django.conf import settings + - follows: + stopBy: end + kind: import_from_statement + pattern: from django.conf import settings + - any: + - inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + pattern: from hashids import Hashids + - follows: + stopBy: end + kind: import_from_statement + pattern: from hashids import Hashids + + hashids.Hashids(salt=settings.SECRET_KEY, min_length=settings.ID_HASH_MIN_LENGTH): + kind: call + all: + - has: + kind: attribute + regex: ^hashids.Hashids$ + - has: + kind: argument_list + nthChild: 2 + has: + kind: keyword_argument + all: + - has: + kind: identifier + regex: ^salt$ + - has: + kind: attribute + all: + - has: + kind: identifier + regex: ^settings$ + nthChild: 1 + - has: + kind: identifier + nthChild: 2 + regex: ^SECRET_KEY$ + - any: + - inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + pattern: from django.conf import settings + - follows: + stopBy: end + kind: import_from_statement + pattern: from django.conf import settings + + hashids.Hashids(settings.SECRET_KEY, min_length=length, alphabet=alphabet): + kind: call + all: + - has: + kind: attribute + nthChild: 1 + regex: ^hashids.Hashids$ + - has: + kind: argument_list + nthChild: 2 + has: + kind: attribute + all: + - has: + kind: identifier + regex: ^settings$ + nthChild: 1 + - has: + kind: identifier + nthChild: 2 + regex: ^SECRET_KEY$ + - all: + - any: + - inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + pattern: from django.conf import settings + - follows: + stopBy: end + kind: import_from_statement + pattern: from django.conf import settings + + hashids.Hashids(salt=django.conf.settings.SECRET_KEY, min_length=settings.ID_HASH_MIN_LENGTH): + kind: call + all: + - has: + kind: attribute + nthChild: 1 + regex: ^hashids.Hashids$ + - has: + kind: argument_list + nthChild: 2 + has: + kind: keyword_argument + all: + - has: + kind: identifier + regex: ^salt$ + - has: + kind: attribute + regex: ^django.conf.settings.SECRET_KEY$ + + hashids.Hashids(django.conf.settings.SECRET_KEY, min_length=length, alphabet=alphabet): + kind: call + all: + - has: + kind: attribute + nthChild: 1 + regex: ^hashids.Hashids$ + - has: + kind: argument_list + nthChild: 2 + has: + kind: attribute + regex: ^django.conf.settings.SECRET_KEY$ + + Hashids(django.conf.settings.SECRET_KEY, min_length=length, alphabet=alphabet): + kind: call + all: + - has: + kind: identifier + nthChild: 1 + regex: ^Hashids$ + - has: + kind: argument_list + nthChild: 2 + has: + kind: attribute + regex: ^django.conf.settings.SECRET_KEY$ + - any: + - inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + pattern: from hashids import Hashids + - follows: + stopBy: end + kind: import_from_statement + pattern: from hashids import Hashids + + Hashids(salt=django.conf.settings.SECRET_KEY, min_length=settings.ID_HASH_MIN_LENGTH): + kind: call + all: + - has: + kind: identifier + nthChild: 1 + regex: ^Hashids$ + - has: + kind: argument_list + nthChild: 2 + has: + kind: keyword_argument + all: + - has: + kind: identifier + regex: ^salt$ + - has: + kind: attribute + regex: ^django.conf.settings.SECRET_KEY$ + - any: + - inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + pattern: from hashids import Hashids + - follows: + stopBy: end + kind: import_from_statement + pattern: from hashids import Hashids + + +rule: + any: + - matches: Hashids(salt=settings.SECRET_KEY, min_length=settings.ID_HASH_MIN_LENGTH) + - matches: Hashids(settings.SECRET_KEY, min_length=length, alphabet=alphabet) + - matches: hashids.Hashids(salt=settings.SECRET_KEY, min_length=settings.ID_HASH_MIN_LENGTH) + - matches: hashids.Hashids(settings.SECRET_KEY, min_length=length, alphabet=alphabet) + - matches: hashids.Hashids(salt=django.conf.settings.SECRET_KEY, min_length=settings.ID_HASH_MIN_LENGTH) + - matches: hashids.Hashids(django.conf.settings.SECRET_KEY, min_length=length, alphabet=alphabet) + - matches: Hashids(django.conf.settings.SECRET_KEY, min_length=length, alphabet=alphabet) + - matches: Hashids(salt=django.conf.settings.SECRET_KEY, min_length=settings.ID_HASH_MIN_LENGTH) \ No newline at end of file diff --git a/tests/__snapshots__/hashids-with-django-secret-python-snapshot.yml b/tests/__snapshots__/hashids-with-django-secret-python-snapshot.yml new file mode 100644 index 00000000..b65472f1 --- /dev/null +++ b/tests/__snapshots__/hashids-with-django-secret-python-snapshot.yml @@ -0,0 +1,252 @@ +id: hashids-with-django-secret-python +snapshots: + ? | + from django.conf import settings + from hashids import Hashids + import hashlib + hashid_1 = Hashids(salt=settings.SECRET_KEY) + : labels: + - source: Hashids(salt=settings.SECRET_KEY) + style: primary + start: 87 + end: 120 + - source: Hashids + style: secondary + start: 87 + end: 94 + - source: salt + style: secondary + start: 95 + end: 99 + - source: settings + style: secondary + start: 100 + end: 108 + - source: SECRET_KEY + style: secondary + start: 109 + end: 119 + - source: settings.SECRET_KEY + style: secondary + start: 100 + end: 119 + - source: salt=settings.SECRET_KEY + style: secondary + start: 95 + end: 119 + - source: (salt=settings.SECRET_KEY) + style: secondary + start: 94 + end: 120 + - source: from django.conf import settings + style: secondary + start: 0 + end: 32 + - source: from django.conf import settings + style: secondary + start: 0 + end: 32 + - source: from hashids import Hashids + style: secondary + start: 33 + end: 60 + - source: from hashids import Hashids + style: secondary + start: 33 + end: 60 + ? "from django.conf import settings\nfrom hashids import Hashids\nimport hashlib\nhashid_2 = Hashids(salt=settings.SECRET_KEY, min_length=8) \n" + : labels: + - source: Hashids(salt=settings.SECRET_KEY, min_length=8) + style: primary + start: 87 + end: 134 + - source: Hashids + style: secondary + start: 87 + end: 94 + - source: salt + style: secondary + start: 95 + end: 99 + - source: settings + style: secondary + start: 100 + end: 108 + - source: SECRET_KEY + style: secondary + start: 109 + end: 119 + - source: settings.SECRET_KEY + style: secondary + start: 100 + end: 119 + - source: salt=settings.SECRET_KEY + style: secondary + start: 95 + end: 119 + - source: (salt=settings.SECRET_KEY, min_length=8) + style: secondary + start: 94 + end: 134 + - source: from django.conf import settings + style: secondary + start: 0 + end: 32 + - source: from django.conf import settings + style: secondary + start: 0 + end: 32 + - source: from hashids import Hashids + style: secondary + start: 33 + end: 60 + - source: from hashids import Hashids + style: secondary + start: 33 + end: 60 + ? | + from django.conf import settings + from hashids import Hashids + import hashlib + hashid_3 = Hashids(settings.SECRET_KEY, min_length=10) + : labels: + - source: Hashids(settings.SECRET_KEY, min_length=10) + style: primary + start: 87 + end: 130 + - source: Hashids + style: secondary + start: 87 + end: 94 + - source: settings + style: secondary + start: 95 + end: 103 + - source: SECRET_KEY + style: secondary + start: 104 + end: 114 + - source: settings.SECRET_KEY + style: secondary + start: 95 + end: 114 + - source: (settings.SECRET_KEY, min_length=10) + style: secondary + start: 94 + end: 130 + - source: from django.conf import settings + style: secondary + start: 0 + end: 32 + - source: from django.conf import settings + style: secondary + start: 0 + end: 32 + - source: from hashids import Hashids + style: secondary + start: 33 + end: 60 + - source: from hashids import Hashids + style: secondary + start: 33 + end: 60 + ? | + from django.conf import settings + from hashids import Hashids + import hashlib + hashid_4 = Hashids(settings.SECRET_KEY, alphabet="1234567890abcdef") + : labels: + - source: Hashids(settings.SECRET_KEY, alphabet="1234567890abcdef") + style: primary + start: 87 + end: 144 + - source: Hashids + style: secondary + start: 87 + end: 94 + - source: settings + style: secondary + start: 95 + end: 103 + - source: SECRET_KEY + style: secondary + start: 104 + end: 114 + - source: settings.SECRET_KEY + style: secondary + start: 95 + end: 114 + - source: (settings.SECRET_KEY, alphabet="1234567890abcdef") + style: secondary + start: 94 + end: 144 + - source: from django.conf import settings + style: secondary + start: 0 + end: 32 + - source: from django.conf import settings + style: secondary + start: 0 + end: 32 + - source: from hashids import Hashids + style: secondary + start: 33 + end: 60 + - source: from hashids import Hashids + style: secondary + start: 33 + end: 60 + ? | + from django.conf import settings + from hashids import Hashids + import hashlib + hashid_5 = Hashids(salt=settings.SECRET_KEY, min_length=12, alphabet="abcdef") + : labels: + - source: Hashids(salt=settings.SECRET_KEY, min_length=12, alphabet="abcdef") + style: primary + start: 87 + end: 154 + - source: Hashids + style: secondary + start: 87 + end: 94 + - source: salt + style: secondary + start: 95 + end: 99 + - source: settings + style: secondary + start: 100 + end: 108 + - source: SECRET_KEY + style: secondary + start: 109 + end: 119 + - source: settings.SECRET_KEY + style: secondary + start: 100 + end: 119 + - source: salt=settings.SECRET_KEY + style: secondary + start: 95 + end: 119 + - source: (salt=settings.SECRET_KEY, min_length=12, alphabet="abcdef") + style: secondary + start: 94 + end: 154 + - source: from django.conf import settings + style: secondary + start: 0 + end: 32 + - source: from django.conf import settings + style: secondary + start: 0 + end: 32 + - source: from hashids import Hashids + style: secondary + start: 33 + end: 60 + - source: from hashids import Hashids + style: secondary + start: 33 + end: 60 diff --git a/tests/__snapshots__/ruby-mysql2-empty-password-ruby-snapshot.yml b/tests/__snapshots__/ruby-mysql2-empty-password-ruby-snapshot.yml index cd7c9aa9..8cd6162a 100644 --- a/tests/__snapshots__/ruby-mysql2-empty-password-ruby-snapshot.yml +++ b/tests/__snapshots__/ruby-mysql2-empty-password-ruby-snapshot.yml @@ -1,5 +1,71 @@ id: ruby-mysql2-empty-password-ruby snapshots: + ? | + $LOAD_PATH.unshift 'lib' + require 'mysql2' + require 'timeout' + Mysql2::Client.new(host: "localhost", username: "root", password: "").query("SELECT sleep(#{overhead}) as result") + : labels: + - source: 'Mysql2::Client.new(host: "localhost", username: "root", password: "")' + style: primary + start: 60 + end: 129 + - source: Mysql2 + style: secondary + start: 60 + end: 66 + - source: Client + style: secondary + start: 68 + end: 74 + - source: Mysql2::Client + style: secondary + start: 60 + end: 74 + - source: new + style: secondary + start: 75 + end: 78 + - source: password + style: secondary + start: 116 + end: 124 + - source: '""' + style: secondary + start: 126 + end: 128 + - source: 'password: ""' + style: secondary + start: 116 + end: 128 + - source: '(host: "localhost", username: "root", password: "")' + style: secondary + start: 78 + end: 129 + - source: require + style: secondary + start: 25 + end: 32 + - source: mysql2 + style: secondary + start: 34 + end: 40 + - source: '''mysql2''' + style: secondary + start: 33 + end: 41 + - source: '''mysql2''' + style: secondary + start: 33 + end: 41 + - source: require 'mysql2' + style: secondary + start: 25 + end: 41 + - source: require 'mysql2' + style: secondary + start: 25 + end: 41 ? | $LOAD_PATH.unshift 'lib' require 'mysql2' diff --git a/tests/python/hashids-with-django-secret-python-test.yml b/tests/python/hashids-with-django-secret-python-test.yml new file mode 100644 index 00000000..2a40985a --- /dev/null +++ b/tests/python/hashids-with-django-secret-python-test.yml @@ -0,0 +1,33 @@ +id: hashids-with-django-secret-python +valid: + - | + from django.conf import settings + from hashids import Hashids + import hashlib + hashid_6 = Hashids(salt="custom_salt_123", min_length=6) +invalid: + - | + from django.conf import settings + from hashids import Hashids + import hashlib + hashid_1 = Hashids(salt=settings.SECRET_KEY) + - | + from django.conf import settings + from hashids import Hashids + import hashlib + hashid_2 = Hashids(salt=settings.SECRET_KEY, min_length=8) + - | + from django.conf import settings + from hashids import Hashids + import hashlib + hashid_3 = Hashids(settings.SECRET_KEY, min_length=10) + - | + from django.conf import settings + from hashids import Hashids + import hashlib + hashid_4 = Hashids(settings.SECRET_KEY, alphabet="1234567890abcdef") + - | + from django.conf import settings + from hashids import Hashids + import hashlib + hashid_5 = Hashids(salt=settings.SECRET_KEY, min_length=12, alphabet="abcdef") From 7a0cb6f50c7b65683afe35a977378b516ffa93eb Mon Sep 17 00:00:00 2001 From: Sakshis Date: Wed, 5 Mar 2025 11:03:54 +0000 Subject: [PATCH 2/4] python-couchbase-hardcoded-secret-python --- ...thon-couchbase-hardcoded-secret-python.yml | 185 ++++++++++++++++++ ...hbase-hardcoded-secret-python-snapshot.yml | 106 ++++++++++ ...couchbase-hardcoded-secret-python-test.yml | 12 ++ 3 files changed, 303 insertions(+) create mode 100644 rules/python/security/python-couchbase-hardcoded-secret-python.yml create mode 100644 tests/__snapshots__/python-couchbase-hardcoded-secret-python-snapshot.yml create mode 100644 tests/python/python-couchbase-hardcoded-secret-python-test.yml diff --git a/rules/python/security/python-couchbase-hardcoded-secret-python.yml b/rules/python/security/python-couchbase-hardcoded-secret-python.yml new file mode 100644 index 00000000..e1e06bab --- /dev/null +++ b/rules/python/security/python-couchbase-hardcoded-secret-python.yml @@ -0,0 +1,185 @@ +id: python-couchbase-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 + [A07:2021]: Identification and Authentication Failures + [REFERENCES] + - https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html +ast-grep-essentials: true +utils: + define_string: + kind: string + all: + - has: + kind: string_start + nthChild: 1 + - has: + kind: string_content + nthChild: 2 + - has: + kind: string_end + nthChild: 3 + + define_password: + any: + - matches: define_string + - kind: identifier + pattern: $PWD_IDENTIFIER + inside: + stopBy: end + follows: + stopBy: end + kind: expression_statement + has: + stopBy: end + kind: assignment + nthChild: 1 + all: + - has: + nthChild: 1 + kind: identifier + field: left + pattern: $PWD_IDENTIFIER + - has: + nthChild: 2 + matches: define_string + +rule: + any: + - kind: call + any: + - kind: call + has: + kind: identifier + regex: ^PasswordAuthenticator$ + precedes: + kind: argument_list + has: + nthChild: + position: 2 + ofRule: + not: + kind: comment + matches: define_password + inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + all: + - has: + nthChild: 1 + kind: dotted_name + field: module_name + regex: ^couchbase_core.cluster$ + precedes: + stopBy: end + kind: dotted_name + regex: ^PasswordAuthenticator$ + - kind: call + any: + - kind: call + has: + kind: identifier + pattern: $PLAIN_ALIAS + precedes: + kind: argument_list + has: + nthChild: + position: 2 + ofRule: + not: + kind: comment + matches: define_password + inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + all: + - has: + nthChild: 1 + kind: dotted_name + field: module_name + regex: ^couchbase_core.cluster$ + precedes: + stopBy: end + kind: aliased_import + all: + - has: + kind: dotted_name + field: name + nthChild: 1 + regex: ^PasswordAuthenticator$ + - has: + kind: identifier + field: alias + nthChild: 2 + pattern: $PLAIN_ALIAS + - kind: call + any: + - kind: call + has: + kind: attribute + field: function + regex: ^couchbase_core.cluster.PasswordAuthenticator$ + precedes: + kind: argument_list + has: + nthChild: + position: 2 + ofRule: + not: + kind: comment + matches: define_password + inside: + stopBy: end + follows: + stopBy: end + any: + - kind: import_from_statement + has: + nthChild: 1 + kind: dotted_name + field: module_name + regex: ^couchbase_core.cluster$ + - kind: import_statement + has: + nthChild: 1 + kind: dotted_name + field: module_name + regex: ^(couchbase_core|couchbase_core.cluster)$ + - kind: call + any: + - kind: call + has: + kind: attribute + nthChild: 1 + regex: ^cluster.PasswordAuthenticator$ + precedes: + kind: argument_list + has: + nthChild: + position: 2 + ofRule: + not: + kind: comment + matches: define_password + inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + all: + - has: + nthChild: 1 + kind: dotted_name + field: module_name + regex: ^couchbase_core$ + precedes: + stopBy: end + kind: dotted_name + regex: ^cluster$ diff --git a/tests/__snapshots__/python-couchbase-hardcoded-secret-python-snapshot.yml b/tests/__snapshots__/python-couchbase-hardcoded-secret-python-snapshot.yml new file mode 100644 index 00000000..79ced8e4 --- /dev/null +++ b/tests/__snapshots__/python-couchbase-hardcoded-secret-python-snapshot.yml @@ -0,0 +1,106 @@ +id: python-couchbase-hardcoded-secret-python +snapshots: + ? | + from couchbase_core.cluster import PasswordAuthenticator + cluster = Cluster('couchbase://localhost', ClusterOptions(PasswordAuthenticator('username', 'password'))) + : labels: + - source: PasswordAuthenticator('username', 'password') + style: primary + start: 115 + end: 160 + - source: '''' + style: secondary + start: 149 + end: 150 + - source: password + style: secondary + start: 150 + end: 158 + - source: '''' + style: secondary + start: 158 + end: 159 + - source: '''password''' + style: secondary + start: 149 + end: 159 + - source: ('username', 'password') + style: secondary + start: 136 + end: 160 + - source: PasswordAuthenticator + style: secondary + start: 115 + end: 136 + - source: PasswordAuthenticator + style: secondary + start: 35 + end: 56 + - source: couchbase_core.cluster + style: secondary + start: 5 + end: 27 + - source: from couchbase_core.cluster import PasswordAuthenticator + style: secondary + start: 0 + end: 56 + - source: from couchbase_core.cluster import PasswordAuthenticator + style: secondary + start: 0 + end: 56 + ? | + from couchbase_core.cluster import PasswordAuthenticator as abc + cluster = Cluster('couchbase://localhost', ClusterOptions(abc('username', 'password'))) + : labels: + - source: abc('username', 'password') + style: primary + start: 122 + end: 149 + - source: '''' + style: secondary + start: 138 + end: 139 + - source: password + style: secondary + start: 139 + end: 147 + - source: '''' + style: secondary + start: 147 + end: 148 + - source: '''password''' + style: secondary + start: 138 + end: 148 + - source: ('username', 'password') + style: secondary + start: 125 + end: 149 + - source: abc + style: secondary + start: 122 + end: 125 + - source: PasswordAuthenticator + style: secondary + start: 35 + end: 56 + - source: abc + style: secondary + start: 60 + end: 63 + - source: PasswordAuthenticator as abc + style: secondary + start: 35 + end: 63 + - source: couchbase_core.cluster + style: secondary + start: 5 + end: 27 + - source: from couchbase_core.cluster import PasswordAuthenticator as abc + style: secondary + start: 0 + end: 63 + - source: from couchbase_core.cluster import PasswordAuthenticator as abc + style: secondary + start: 0 + end: 63 diff --git a/tests/python/python-couchbase-hardcoded-secret-python-test.yml b/tests/python/python-couchbase-hardcoded-secret-python-test.yml new file mode 100644 index 00000000..18b28ad5 --- /dev/null +++ b/tests/python/python-couchbase-hardcoded-secret-python-test.yml @@ -0,0 +1,12 @@ +id: python-couchbase-hardcoded-secret-python +valid: + - | + from couchbase_core.cluster import PasswordAuthenticator + cluster = Cluster('couchbase://localhost', ClusterOptions(PasswordAuthenticator('username', get_pass()))) +invalid: + - | + from couchbase_core.cluster import PasswordAuthenticator + cluster = Cluster('couchbase://localhost', ClusterOptions(PasswordAuthenticator('username', 'password'))) + - | + from couchbase_core.cluster import PasswordAuthenticator as abc + cluster = Cluster('couchbase://localhost', ClusterOptions(abc('username', 'password'))) From 096a48000fc56cd4e6b11c76749b28c0cc7cfce9 Mon Sep 17 00:00:00 2001 From: Sakshis Date: Wed, 5 Mar 2025 11:06:23 +0000 Subject: [PATCH 3/4] python-cassandra-hardcoded-secret-python --- ...thon-cassandra-hardcoded-secret-python.yml | 398 ++++++++++++++++++ ...cassandra-hardcoded-secret-python-test.yml | 12 + 2 files changed, 410 insertions(+) create mode 100644 rules/python/security/python-cassandra-hardcoded-secret-python.yml create mode 100644 tests/python/python-cassandra-hardcoded-secret-python-test.yml diff --git a/rules/python/security/python-cassandra-hardcoded-secret-python.yml b/rules/python/security/python-cassandra-hardcoded-secret-python.yml new file mode 100644 index 00000000..7968e2d5 --- /dev/null +++ b/rules/python/security/python-cassandra-hardcoded-secret-python.yml @@ -0,0 +1,398 @@ +id: python-cassandra-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 + [A07:2021]: Identification and Authentication Failures + [REFERENCES] + - https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html +ast-grep-essentials: true +utils: + define_string: + kind: string + all: + - has: + kind: string_start + nthChild: 1 + - has: + kind: string_content + nthChild: 2 + - has: + kind: string_end + nthChild: 3 + + define_password: + any: + - matches: define_string + - kind: identifier + pattern: $PWD_IDENTIFIER + inside: + stopBy: end + follows: + stopBy: end + kind: expression_statement + has: + stopBy: end + kind: assignment + nthChild: 1 + all: + - has: + nthChild: 1 + kind: identifier + field: left + pattern: $PWD_IDENTIFIER + - has: + nthChild: 2 + matches: define_string + +rule: + any: + - kind: call + any: + - kind: call + has: + kind: identifier + nthChild: 1 + regex: ^PlainTextAuthProvider$ + precedes: + kind: argument_list + has: + stopBy: end + kind: keyword_argument + all: + - has: + nthChild: 1 + kind: identifier + regex: ^password$ + - has: + nthChild: 2 + matches: define_password + - kind: call + has: + kind: identifier + regex: ^PlainTextAuthProvider$ + precedes: + kind: argument_list + has: + nthChild: + position: 2 + ofRule: + not: + kind: comment + matches: define_password + inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + all: + - has: + nthChild: 1 + kind: dotted_name + field: module_name + regex: ^cassandra.auth$ + precedes: + stopBy: end + kind: dotted_name + regex: ^PlainTextAuthProvider$ + - kind: call + any: + - kind: call + has: + kind: identifier + regex: ^SaslAuthProvider$ + precedes: + kind: argument_list + has: + stopBy: end + kind: keyword_argument + all: + - has: + nthChild: 1 + kind: identifier + regex: ^password$ + - has: + nthChild: 2 + matches: define_password + inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + all: + - has: + nthChild: 1 + kind: dotted_name + field: module_name + regex: ^cassandra.auth$ + precedes: + stopBy: end + kind: dotted_name + regex: ^SaslAuthProvider$ + - kind: call + any: + - kind: call + has: + kind: identifier + pattern: $PLAIN_ALIAS + precedes: + kind: argument_list + has: + stopBy: end + kind: keyword_argument + all: + - has: + nthChild: 1 + kind: identifier + regex: ^password$ + - has: + nthChild: 2 + matches: define_password + - kind: call + has: + kind: identifier + pattern: $PLAIN_ALIAS + precedes: + kind: argument_list + has: + nthChild: + position: 2 + ofRule: + not: + kind: comment + matches: define_password + inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + all: + - has: + nthChild: 1 + kind: dotted_name + field: module_name + regex: ^cassandra.auth$ + precedes: + stopBy: end + kind: aliased_import + all: + - has: + kind: dotted_name + field: name + nthChild: 1 + regex: ^PlainTextAuthProvider$ + - has: + kind: identifier + field: alias + nthChild: 2 + pattern: $PLAIN_ALIAS + - kind: call + any: + - kind: call + has: + kind: identifier + pattern: $SASL_ALIAS + precedes: + kind: argument_list + has: + stopBy: end + kind: keyword_argument + all: + - has: + nthChild: 1 + kind: identifier + regex: ^password$ + - has: + nthChild: 2 + matches: define_password + inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + all: + - has: + nthChild: 1 + kind: dotted_name + field: module_name + regex: ^cassandra.auth$ + precedes: + stopBy: end + kind: aliased_import + all: + - has: + kind: dotted_name + nthChild: 1 + regex: ^SaslAuthProvider$ + - has: + kind: identifier + field: alias + nthChild: 2 + pattern: $SASL_ALIAS + - kind: call + any: + - kind: call + has: + kind: attribute + nthChild: 1 + regex: ^cassandra.auth.PlainTextAuthProvider$ + precedes: + kind: argument_list + has: + stopBy: end + kind: keyword_argument + all: + - has: + nthChild: 1 + kind: identifier + regex: ^password$ + - has: + nthChild: 2 + matches: define_password + - kind: call + has: + kind: attribute + field: function + regex: ^cassandra.auth.PlainTextAuthProvider$ + precedes: + kind: argument_list + has: + nthChild: + position: 2 + ofRule: + not: + kind: comment + matches: define_password + inside: + stopBy: end + follows: + stopBy: end + any: + - kind: import_from_statement + has: + nthChild: 1 + kind: dotted_name + field: module_name + regex: ^cassandra.auth$ + - kind: import_statement + has: + nthChild: 1 + kind: dotted_name + field: module_name + regex: ^(cassandra|cassandra.auth)$ + - kind: call + any: + - kind: call + has: + kind: attribute + regex: ^SaslAuthProvider$ + precedes: + kind: argument_list + has: + stopBy: end + kind: keyword_argument + all: + - has: + nthChild: 1 + kind: identifier + regex: ^password$ + - has: + nthChild: 2 + matches: define_password + inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + all: + - has: + nthChild: 1 + kind: dotted_name + field: module_name + regex: ^cassandra.auth$ + precedes: + stopBy: end + kind: dotted_name + regex: ^SaslAuthProvider$ + - kind: call + any: + - kind: call + has: + kind: attribute + nthChild: 1 + regex: ^auth.PlainTextAuthProvider$ + precedes: + kind: argument_list + has: + stopBy: end + kind: keyword_argument + all: + - has: + nthChild: 1 + kind: identifier + regex: ^password$ + - has: + nthChild: 2 + matches: define_password + - kind: call + has: + kind: attribute + nthChild: 1 + regex: ^auth.PlainTextAuthProvider$ + precedes: + kind: argument_list + has: + nthChild: + position: 2 + ofRule: + not: + kind: comment + matches: define_password + inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + all: + - has: + nthChild: 1 + kind: dotted_name + field: module_name + regex: ^cassandra$ + precedes: + stopBy: end + kind: dotted_name + regex: ^auth$ + - kind: call + any: + - kind: call + has: + kind: attribute + nthChild: 1 + regex: ^auth.SaslAuthProvider$ + precedes: + kind: argument_list + has: + nthChild: + position: 2 + ofRule: + not: + kind: comment + matches: define_password + inside: + stopBy: end + follows: + stopBy: end + kind: import_from_statement + all: + - has: + nthChild: 1 + kind: dotted_name + field: module_name + regex: ^cassandra$ + precedes: + stopBy: end + kind: dotted_name + regex: ^auth$ diff --git a/tests/python/python-cassandra-hardcoded-secret-python-test.yml b/tests/python/python-cassandra-hardcoded-secret-python-test.yml new file mode 100644 index 00000000..fe353f30 --- /dev/null +++ b/tests/python/python-cassandra-hardcoded-secret-python-test.yml @@ -0,0 +1,12 @@ +id: python-cassandra-hardcoded-secret-python +valid: + - | + from cassandra.auth import PlainTextAuthProvider + auth_provider = PlainTextAuthProvider('user', '') +invalid: + - | + from cassandra.auth import PlainTextAuthProvider + auth_provider = PlainTextAuthProvider('user', 'pass') + - | + from cassandra.auth import PlainTextAuthProvider + auth_provider = PlainTextAuthProvider(username='user', password='pass') From 1eb473a5b3b867fb41490d3c4978cb1e3c437187 Mon Sep 17 00:00:00 2001 From: Sakshis Date: Wed, 5 Mar 2025 11:06:48 +0000 Subject: [PATCH 4/4] python-cassandra-hardcoded-secret-python --- ...andra-hardcoded-secret-python-snapshot.yml | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 tests/__snapshots__/python-cassandra-hardcoded-secret-python-snapshot.yml diff --git a/tests/__snapshots__/python-cassandra-hardcoded-secret-python-snapshot.yml b/tests/__snapshots__/python-cassandra-hardcoded-secret-python-snapshot.yml new file mode 100644 index 00000000..e90adeab --- /dev/null +++ b/tests/__snapshots__/python-cassandra-hardcoded-secret-python-snapshot.yml @@ -0,0 +1,106 @@ +id: python-cassandra-hardcoded-secret-python +snapshots: + ? | + from cassandra.auth import PlainTextAuthProvider + auth_provider = PlainTextAuthProvider('user', 'pass') + : labels: + - source: PlainTextAuthProvider('user', 'pass') + style: primary + start: 65 + end: 102 + - source: '''' + style: secondary + start: 95 + end: 96 + - source: pass + style: secondary + start: 96 + end: 100 + - source: '''' + style: secondary + start: 100 + end: 101 + - source: '''pass''' + style: secondary + start: 95 + end: 101 + - source: ('user', 'pass') + style: secondary + start: 86 + end: 102 + - source: PlainTextAuthProvider + style: secondary + start: 65 + end: 86 + - source: PlainTextAuthProvider + style: secondary + start: 27 + end: 48 + - source: cassandra.auth + style: secondary + start: 5 + end: 19 + - source: from cassandra.auth import PlainTextAuthProvider + style: secondary + start: 0 + end: 48 + - source: from cassandra.auth import PlainTextAuthProvider + style: secondary + start: 0 + end: 48 + ? | + from cassandra.auth import PlainTextAuthProvider + auth_provider = PlainTextAuthProvider(username='user', password='pass') + : labels: + - source: PlainTextAuthProvider(username='user', password='pass') + style: primary + start: 65 + end: 120 + - source: password + style: secondary + start: 104 + end: 112 + - source: '''' + style: secondary + start: 113 + end: 114 + - source: pass + style: secondary + start: 114 + end: 118 + - source: '''' + style: secondary + start: 118 + end: 119 + - source: '''pass''' + style: secondary + start: 113 + end: 119 + - source: password='pass' + style: secondary + start: 104 + end: 119 + - source: (username='user', password='pass') + style: secondary + start: 86 + end: 120 + - source: PlainTextAuthProvider + style: secondary + start: 65 + end: 86 + - source: PlainTextAuthProvider + style: secondary + start: 27 + end: 48 + - source: cassandra.auth + style: secondary + start: 5 + end: 19 + - source: from cassandra.auth import PlainTextAuthProvider + style: secondary + start: 0 + end: 48 + - source: from cassandra.auth import PlainTextAuthProvider + style: secondary + start: 0 + end: 48