diff --git a/rules/ruby/security/ruby-cassandra-empty-password-ruby.yml b/rules/ruby/security/ruby-cassandra-empty-password-ruby.yml new file mode 100644 index 00000000..d1218655 --- /dev/null +++ b/rules/ruby/security/ruby-cassandra-empty-password-ruby.yml @@ -0,0 +1,151 @@ +id: ruby-cassandra-empty-password-ruby +language: ruby +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. + [REFERENCES] + - https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html + +ast-grep-essentials: true + +utils: + Cassandra.cluster(): + # Cassandra.cluster(..., password: "", ...) + kind: call + all: + - has: + stopBy: neighbor + kind: constant + regex: ^Cassandra$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^cluster$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: pair + all: + - has: + stopBy: neighbor + any: + - kind: hash_key_symbol + regex: ^password$ + - kind: simple_symbol + regex: ^:password$ + - has: + stopBy: neighbor + kind: string + not: + has: + kind: string_content + - any: + - inside: + stopBy: end + follows: + stopBy: end + kind: call + pattern: require 'cassandra' + - follows: + stopBy: end + kind: call + pattern: require 'cassandra' + + Cassandra.cluster()_Instance: + # Cassandra.cluster(..., password: "", ...) + kind: call + all: + - has: + stopBy: neighbor + kind: constant + regex: ^Cassandra$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^cluster$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: pair + all: + - has: + stopBy: neighbor + any: + - kind: hash_key_symbol + regex: ^password$ + - kind: simple_symbol + regex: ^:password$ + - has: + stopBy: neighbor + kind: identifier + pattern: $VAR + - any: + - inside: + stopBy: end + follows: + stopBy: end + kind: call + pattern: require 'cassandra' + - follows: + stopBy: end + kind: call + pattern: require 'cassandra' + - any: + - follows: + stopBy: end + kind: assignment + all: + - has: + kind: identifier + pattern: $VAR + - has: + kind: string + not: + has: + kind: string_content + - inside: + stopBy: end + follows: + stopBy: end + kind: assignment + all: + - has: + kind: identifier + pattern: $VAR + - has: + kind: string + not: + has: + kind: string_content + +rule: + kind: call + any: + - matches: Cassandra.cluster() + - matches: Cassandra.cluster()_Instance + not: + all: + - has: + stopBy: end + kind: ERROR + - inside: + stopBy: end + kind: ERROR + diff --git a/rules/ruby/security/ruby-cassandra-hardcoded-secret-ruby.yml b/rules/ruby/security/ruby-cassandra-hardcoded-secret-ruby.yml new file mode 100644 index 00000000..f7a69977 --- /dev/null +++ b/rules/ruby/security/ruby-cassandra-hardcoded-secret-ruby.yml @@ -0,0 +1,147 @@ +id: ruby-cassandra-hardcoded-secret-ruby +language: ruby +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. + [REFERENCES] + - https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html + +ast-grep-essentials: true + +utils: + Cassandra.cluster(): + # Cassandra.cluster(..., password: "", ...) + kind: call + all: + - has: + stopBy: neighbor + kind: constant + regex: ^Cassandra$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^cluster$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: pair + all: + - has: + stopBy: neighbor + any: + - kind: hash_key_symbol + regex: ^password$ + - kind: simple_symbol + regex: ^:password$ + - has: + stopBy: neighbor + kind: string + has: + stopBy: neighbor + kind: string_content + - any: + - inside: + stopBy: end + follows: + stopBy: end + kind: call + pattern: require 'cassandra' + - follows: + stopBy: end + kind: call + pattern: require 'cassandra' + + Cassandra.cluster()_Instance: + # Cassandra.cluster(..., password: "", ...) + kind: call + all: + - has: + stopBy: neighbor + kind: constant + regex: ^Cassandra$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^cluster$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: pair + all: + - has: + stopBy: neighbor + any: + - kind: hash_key_symbol + regex: ^password$ + - kind: simple_symbol + regex: ^:password$ + - has: + stopBy: neighbor + kind: identifier + pattern: $VAR + - any: + - inside: + stopBy: end + follows: + stopBy: end + kind: call + pattern: require 'cassandra' + - follows: + stopBy: end + kind: call + pattern: require 'cassandra' + - any: + - follows: + stopBy: end + kind: assignment + all: + - has: + kind: identifier + pattern: $VAR + - has: + kind: string + has: + kind: string_content + - inside: + stopBy: end + follows: + stopBy: end + kind: assignment + all: + - has: + kind: identifier + pattern: $VAR + - has: + kind: string + has: + kind: string_content +rule: + kind: call + any: + - matches: Cassandra.cluster() + - matches: Cassandra.cluster()_Instance + not: + all: + - has: + stopBy: end + kind: ERROR + - inside: + stopBy: end + kind: ERROR + diff --git a/tests/__snapshots__/ruby-cassandra-empty-password-ruby-snapshot.yml b/tests/__snapshots__/ruby-cassandra-empty-password-ruby-snapshot.yml new file mode 100644 index 00000000..b0021f62 --- /dev/null +++ b/tests/__snapshots__/ruby-cassandra-empty-password-ruby-snapshot.yml @@ -0,0 +1,107 @@ +id: ruby-cassandra-empty-password-ruby +snapshots: + ? | + require 'cassandra' + cluster = Cassandra.cluster(username: 'user',password: '') + : labels: + - source: 'Cassandra.cluster(username: ''user'',password: '''')' + style: primary + start: 30 + end: 78 + - source: Cassandra + style: secondary + start: 30 + end: 39 + - source: . + style: secondary + start: 39 + end: 40 + - source: cluster + style: secondary + start: 40 + end: 47 + - source: password + style: secondary + start: 65 + end: 73 + - source: '''''' + style: secondary + start: 75 + end: 77 + - source: 'password: ''''' + style: secondary + start: 65 + end: 77 + - source: '(username: ''user'',password: '''')' + style: secondary + start: 47 + end: 78 + - source: require 'cassandra' + style: secondary + start: 0 + end: 19 + - source: require 'cassandra' + style: secondary + start: 0 + end: 19 + ? | + require 'cassandra' + password = '' + cluster = Cassandra.cluster(username: 'user',password: password) + : labels: + - source: 'Cassandra.cluster(username: ''user'',password: password)' + style: primary + start: 44 + end: 98 + - source: Cassandra + style: secondary + start: 44 + end: 53 + - source: . + style: secondary + start: 53 + end: 54 + - source: cluster + style: secondary + start: 54 + end: 61 + - source: password + style: secondary + start: 79 + end: 87 + - source: password + style: secondary + start: 89 + end: 97 + - source: 'password: password' + style: secondary + start: 79 + end: 97 + - source: '(username: ''user'',password: password)' + style: secondary + start: 61 + end: 98 + - source: require 'cassandra' + style: secondary + start: 0 + end: 19 + - source: require 'cassandra' + style: secondary + start: 0 + end: 19 + - source: password + style: secondary + start: 20 + end: 28 + - source: '''''' + style: secondary + start: 31 + end: 33 + - source: password = '' + style: secondary + start: 20 + end: 33 + - source: password = '' + style: secondary + start: 20 + end: 33 diff --git a/tests/ruby/ruby-cassandra-empty-password-ruby-test.yml b/tests/ruby/ruby-cassandra-empty-password-ruby-test.yml new file mode 100644 index 00000000..3261b17a --- /dev/null +++ b/tests/ruby/ruby-cassandra-empty-password-ruby-test.yml @@ -0,0 +1,12 @@ +id: ruby-cassandra-empty-password-ruby +valid: + - | + cluster = Cassandra.cluster(username: 'user',password: '') +invalid: + - | + require 'cassandra' + cluster = Cassandra.cluster(username: 'user',password: '') + - | + require 'cassandra' + password = '' + cluster = Cassandra.cluster(username: 'user',password: password) diff --git a/tests/ruby/ruby-cassandra-hardcoded-secret-ruby-test.yml b/tests/ruby/ruby-cassandra-hardcoded-secret-ruby-test.yml new file mode 100644 index 00000000..6b68c674 --- /dev/null +++ b/tests/ruby/ruby-cassandra-hardcoded-secret-ruby-test.yml @@ -0,0 +1,12 @@ +id: ruby-cassandra-hardcoded-secret-ruby +valid: + - | + cluster = Cassandra.cluster(username: 'user',password: '') +invalid: + - | + require 'cassandra' + cluster = Cassandra.cluster( username: 'user',password: 'password') + - | + require 'cassandra' + password = 'password' + cluster = Cassandra.cluster( username: 'user',password: password)