From 3d362f7c8e045b042e179c8509977685aad55360 Mon Sep 17 00:00:00 2001 From: Sakshis Date: Wed, 11 Dec 2024 10:51:38 +0000 Subject: [PATCH 1/3] ruby-pg-empty-password-ruby --- .../security/ruby-pg-empty-password-ruby.yml | 274 ++++++++++++++++ .../ruby-pg-empty-password-ruby-snapshot.yml | 309 ++++++++++++++++++ .../ruby/ruby-pg-empty-password-ruby-test.yml | 44 +++ 3 files changed, 627 insertions(+) create mode 100644 rules/ruby/security/ruby-pg-empty-password-ruby.yml create mode 100644 tests/__snapshots__/ruby-pg-empty-password-ruby-snapshot.yml create mode 100644 tests/ruby/ruby-pg-empty-password-ruby-test.yml diff --git a/rules/ruby/security/ruby-pg-empty-password-ruby.yml b/rules/ruby/security/ruby-pg-empty-password-ruby.yml new file mode 100644 index 00000000..ac674015 --- /dev/null +++ b/rules/ruby/security/ruby-pg-empty-password-ruby.yml @@ -0,0 +1,274 @@ +id: ruby-pg-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 +utils: + PG.connect(password:""): + # PG.connect(..., password: "", ...) + kind: call + all: + - has: + stopBy: neighbor + kind: constant + regex: ^PG$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^connect$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: end + kind: pair + all: + - has: + stopBy: neighbor + regex: ^:password|password$ + - has: + stopBy: neighbor + kind: string + not: + has: + stopBy: neighbor + kind: string_content + PG.connect($HOST, $PORT, $OPS, $TTY, $DB, $USER, ""): + # PG.connect($HOST, $PORT, $OPS, $TTY, $DB, $USER, "", ...) + kind: call + all: + - has: + stopBy: neighbor + kind: constant + regex: ^PG$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^connect$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: string + nthChild: 7 + not: + has: + stopBy: neighbor + kind: string_content + PG.connect($HOST, $PORT, $OPS, $TTY, $DB, $USER, "")_with_instance: + # PG.connect($HOST, $PORT, $OPS, $TTY, $DB, $USER, "")_with_instance + kind: call + all: + - has: + stopBy: neighbor + kind: constant + regex: ^PG$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^connect$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: identifier + pattern: $PASS + nthChild: 7 + - inside: + stopBy: end + kind: program + has: + stopBy: end + kind: assignment + pattern: $PASS = '' + PG.connect(password:"")_with_instance: + # PG.connect(..., password: "", ...) + kind: call + all: + - has: + stopBy: neighbor + kind: constant + regex: ^PG$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^connect$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: end + kind: pair + all: + - has: + stopBy: neighbor + regex: ^password|:password$ + - has: + stopBy: neighbor + kind: identifier + pattern: $PASS + - inside: + stopBy: end + kind: program + has: + stopBy: end + kind: assignment + pattern: $PASS = '' + PG::Connection.new(password:"")_with_instance: + # PG::Connection.new(..., password: '', ...) + kind: call + all: + - has: + stopBy: neighbor + kind: scope_resolution + regex: ^PG::Connection$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^new|connect_start$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: end + kind: pair + all: + - has: + stopBy: neighbor + regex: ^password|:password$ + - has: + stopBy: neighbor + kind: identifier + pattern: $PASS + - inside: + stopBy: end + kind: program + has: + stopBy: end + kind: assignment + pattern: $PASS = '' + PG::Connection.new($HOST, $PORT, $OPS, $TTY, $DB, $USER, ""): + # PG::Connection.connect_start($HOST, $PORT, $OPS, $TTY, $DB, $USER,"", ...) + kind: call + all: + - has: + stopBy: neighbor + kind: scope_resolution + regex: ^PG::Connection$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^connect_start|new$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: string + nthChild: 7 + not: + has: + stopBy: neighbor + kind: string_content + PG::Connection.new($HOST, $PORT, $OPS, $TTY, $DB, $USER, "")_with_instance: + # PG::Connection.connect_start($HOST, $PORT, $OPS, $TTY, $DB, $USER,"", ...) + kind: call + all: + - has: + stopBy: neighbor + kind: scope_resolution + regex: ^PG::Connection$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^connect_start|new$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: identifier + nthChild: 7 + pattern: $PASS + - inside: + stopBy: end + kind: program + has: + stopBy: end + kind: assignment + pattern: $PASS = '' + PG::Connection.new(password:""): + # PG::Connection.new(..., password: '', ...) + kind: call + all: + - has: + stopBy: neighbor + kind: scope_resolution + regex: ^PG::Connection$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^new|connect_start$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: end + kind: pair + all: + - has: + stopBy: neighbor + regex: ^password|:password$ + - has: + stopBy: neighbor + kind: string + not: + has: + stopBy: neighbor + kind: string_content +rule: + kind: call + any: + - matches: PG.connect(password:"") + - matches: PG.connect(password:"")_with_instance + - matches: PG.connect($HOST, $PORT, $OPS, $TTY, $DB, $USER, "") + - matches: PG.connect($HOST, $PORT, $OPS, $TTY, $DB, $USER, "")_with_instance + - matches: PG::Connection.new(password:"")_with_instance + - matches: PG::Connection.new($HOST, $PORT, $OPS, $TTY, $DB, $USER, "") + - matches: PG::Connection.new($HOST, $PORT, $OPS, $TTY, $DB, $USER, "")_with_instance + - matches: PG::Connection.new(password:"") diff --git a/tests/__snapshots__/ruby-pg-empty-password-ruby-snapshot.yml b/tests/__snapshots__/ruby-pg-empty-password-ruby-snapshot.yml new file mode 100644 index 00000000..0dd737e0 --- /dev/null +++ b/tests/__snapshots__/ruby-pg-empty-password-ruby-snapshot.yml @@ -0,0 +1,309 @@ +id: ruby-pg-empty-password-ruby +snapshots: + ? | + con1 = PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => '', + :sslmode => 'prefer' + ) + : labels: + - source: |- + PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => '', + :sslmode => 'prefer' + ) + style: primary + start: 7 + end: 145 + - source: PG + style: secondary + start: 7 + end: 9 + - source: . + style: secondary + start: 9 + end: 10 + - source: connect + style: secondary + start: 10 + end: 17 + - source: :password + style: secondary + start: 105 + end: 114 + - source: '''''' + style: secondary + start: 118 + end: 120 + - source: :password => '' + style: secondary + start: 105 + end: 120 + - source: |- + ( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => '', + :sslmode => 'prefer' + ) + style: secondary + start: 17 + end: 145 + ? | + con3 = PG::.connect nil, 5432, nil, nil, 'test', 'user', '' ) + : labels: + - source: PG::.connect nil, 5432, nil, nil, 'test', 'user', '' + style: primary + start: 7 + end: 59 + - source: PG + style: secondary + start: 7 + end: 9 + - source: . + style: secondary + start: 11 + end: 12 + - source: connect + style: secondary + start: 12 + end: 19 + - source: '''''' + style: secondary + start: 57 + end: 59 + - source: nil, 5432, nil, nil, 'test', 'user', '' + style: secondary + start: 20 + end: 59 + ? | + con3 = PG::Connection.connect_start( nil, 5432, nil, nil, 'test', 'user', '' ) + : labels: + - source: PG::Connection.connect_start( nil, 5432, nil, nil, 'test', 'user', '' ) + style: primary + start: 7 + end: 78 + - source: PG::Connection + style: secondary + start: 7 + end: 21 + - source: . + style: secondary + start: 21 + end: 22 + - source: connect_start + style: secondary + start: 22 + end: 35 + - source: '''''' + style: secondary + start: 74 + end: 76 + - source: ( nil, 5432, nil, nil, 'test', 'user', '' ) + style: secondary + start: 35 + end: 78 + ? | + con3 = PG::Connection.new( nil, 5432, nil, nil, 'test', 'user', '' ) + : labels: + - source: PG::Connection.new( nil, 5432, nil, nil, 'test', 'user', '' ) + style: primary + start: 7 + end: 68 + - source: PG::Connection + style: secondary + start: 7 + end: 21 + - source: . + style: secondary + start: 21 + end: 22 + - source: new + style: secondary + start: 22 + end: 25 + - source: '''''' + style: secondary + start: 64 + end: 66 + - source: ( nil, 5432, nil, nil, 'test', 'user', '' ) + style: secondary + start: 25 + end: 68 + ? | + pw = '' + con1 = PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => pw, + :sslmode => 'prefer' + ) + : labels: + - source: |- + PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => pw, + :sslmode => 'prefer' + ) + style: primary + start: 15 + end: 153 + - source: PG + style: secondary + start: 15 + end: 17 + - source: . + style: secondary + start: 17 + end: 18 + - source: connect + style: secondary + start: 18 + end: 25 + - source: :password + style: secondary + start: 113 + end: 122 + - source: pw + style: secondary + start: 126 + end: 128 + - source: :password => pw + style: secondary + start: 113 + end: 128 + - source: |- + ( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => pw, + :sslmode => 'prefer' + ) + style: secondary + start: 25 + end: 153 + - source: pw = '' + style: secondary + start: 0 + end: 7 + - source: | + pw = '' + con1 = PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => pw, + :sslmode => 'prefer' + ) + style: secondary + start: 0 + end: 154 + ? | + pw = '' + con2 = PG::Connection.connect_start( dbname: 'test', port: 5432, user: 'user', password: pw ) + : labels: + - source: 'PG::Connection.connect_start( dbname: ''test'', port: 5432, user: ''user'', password: pw )' + style: primary + start: 15 + end: 101 + - source: PG::Connection + style: secondary + start: 15 + end: 29 + - source: . + style: secondary + start: 29 + end: 30 + - source: connect_start + style: secondary + start: 30 + end: 43 + - source: password + style: secondary + start: 87 + end: 95 + - source: pw + style: secondary + start: 97 + end: 99 + - source: 'password: pw' + style: secondary + start: 87 + end: 99 + - source: '( dbname: ''test'', port: 5432, user: ''user'', password: pw )' + style: secondary + start: 43 + end: 101 + - source: pw = '' + style: secondary + start: 0 + end: 7 + - source: | + pw = '' + con2 = PG::Connection.connect_start( dbname: 'test', port: 5432, user: 'user', password: pw ) + style: secondary + start: 0 + end: 102 + ? | + pw = '' + con2 = PG::Connection.new( dbname: 'test', port: 5432, user: 'user', password: pw ) + : labels: + - source: 'PG::Connection.new( dbname: ''test'', port: 5432, user: ''user'', password: pw )' + style: primary + start: 15 + end: 91 + - source: PG::Connection + style: secondary + start: 15 + end: 29 + - source: . + style: secondary + start: 29 + end: 30 + - source: new + style: secondary + start: 30 + end: 33 + - source: password + style: secondary + start: 77 + end: 85 + - source: pw + style: secondary + start: 87 + end: 89 + - source: 'password: pw' + style: secondary + start: 77 + end: 89 + - source: '( dbname: ''test'', port: 5432, user: ''user'', password: pw )' + style: secondary + start: 33 + end: 91 + - source: pw = '' + style: secondary + start: 0 + end: 7 + - source: | + pw = '' + con2 = PG::Connection.new( dbname: 'test', port: 5432, user: 'user', password: pw ) + style: secondary + start: 0 + end: 92 diff --git a/tests/ruby/ruby-pg-empty-password-ruby-test.yml b/tests/ruby/ruby-pg-empty-password-ruby-test.yml new file mode 100644 index 00000000..f89611ab --- /dev/null +++ b/tests/ruby/ruby-pg-empty-password-ruby-test.yml @@ -0,0 +1,44 @@ +id: ruby-pg-empty-password-ruby +valid: + - | + con1 = PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => 'password', + :sslmode => 'prefer' + ) +invalid: + - | + con1 = PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => '', + :sslmode => 'prefer' + ) + - | + pw = '' + con2 = PG::Connection.connect_start( dbname: 'test', port: 5432, user: 'user', password: pw ) + - | + con3 = PG::Connection.new( nil, 5432, nil, nil, 'test', 'user', '' ) + - | + con3 = PG::Connection.connect_start( nil, 5432, nil, nil, 'test', 'user', '' ) + - | + pw = '' + con2 = PG::Connection.new( dbname: 'test', port: 5432, user: 'user', password: pw ) + - | + pw = '' + con1 = PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => pw, + :sslmode => 'prefer' + ) + - | + con3 = PG::.connect nil, 5432, nil, nil, 'test', 'user', '' ) + From cf68907af3065361b9924ed5b27d2d1c58644132 Mon Sep 17 00:00:00 2001 From: Sakshis Date: Wed, 11 Dec 2024 10:54:04 +0000 Subject: [PATCH 2/3] ruby-pg-hardcoded-secret-ruby --- .../ruby-pg-hardcoded-secret-ruby.yml | 269 +++++++++++++++ ...ruby-pg-hardcoded-secret-ruby-snapshot.yml | 325 ++++++++++++++++++ .../ruby-pg-hardcoded-secret-ruby-test.yml | 44 +++ 3 files changed, 638 insertions(+) create mode 100644 rules/ruby/security/ruby-pg-hardcoded-secret-ruby.yml create mode 100644 tests/__snapshots__/ruby-pg-hardcoded-secret-ruby-snapshot.yml create mode 100644 tests/ruby/ruby-pg-hardcoded-secret-ruby-test.yml diff --git a/rules/ruby/security/ruby-pg-hardcoded-secret-ruby.yml b/rules/ruby/security/ruby-pg-hardcoded-secret-ruby.yml new file mode 100644 index 00000000..e965d518 --- /dev/null +++ b/rules/ruby/security/ruby-pg-hardcoded-secret-ruby.yml @@ -0,0 +1,269 @@ +id: ruby-pg-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 +utils: + PG.connect(password:""): + # PG::Connection.new(..., password: '', ...) + kind: call + all: + - has: + stopBy: neighbor + kind: constant + regex: ^PG$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^connect$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: end + kind: pair + all: + - has: + stopBy: neighbor + regex: ^password|:password$ + - has: + stopBy: neighbor + kind: string + has: + stopBy: neighbor + kind: string_content + PG.connect($HOST, $PORT, $OPS, $TTY, $DB, $USER, ""): + # PG.connect($HOST, $PORT, $OPS, $TTY, $DB, $USER, "", ...) + kind: call + all: + - has: + stopBy: neighbor + kind: constant + regex: ^PG$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^connect$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: string + nthChild: 7 + has: + stopBy: neighbor + kind: string_content + PG.connect($HOST, $PORT, $OPS, $TTY, $DB, $USER, "")_with_instance: + # PG.connect($HOST, $PORT, $OPS, $TTY, $DB, $USER, "")_with_instance + kind: call + all: + - has: + stopBy: neighbor + kind: constant + regex: ^PG$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^connect$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: identifier + pattern: $PASS + nthChild: 7 + - inside: + stopBy: end + kind: program + has: + stopBy: end + kind: assignment + pattern: $PASS = '$$$' + PG.connect(password:"")_with_instance: + # PG.connect(..., password: "", ...) + kind: call + all: + - has: + stopBy: neighbor + kind: constant + regex: ^PG$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^connect$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: end + kind: pair + all: + - has: + stopBy: neighbor + regex: ^password|:password$ + - has: + stopBy: neighbor + kind: identifier + pattern: $PASS + - inside: + stopBy: end + kind: program + has: + stopBy: end + kind: assignment + pattern: $PASS = '$$$' + PG::Connection.new(password:"")_with_instance: + # PG::Connection.new(..., password: '', ...) + kind: call + all: + - has: + stopBy: neighbor + kind: scope_resolution + regex: ^PG::Connection$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^new|connect_start$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: end + kind: pair + all: + - has: + stopBy: neighbor + regex: ^password|:password$ + - has: + stopBy: neighbor + kind: identifier + pattern: $PASS + - inside: + stopBy: end + kind: program + has: + stopBy: end + kind: assignment + pattern: $PASS = '$$$' + PG::Connection.new($HOST, $PORT, $OPS, $TTY, $DB, $USER, ""): + # PG::Connection.connect_start($HOST, $PORT, $OPS, $TTY, $DB, $USER,"", ...) + kind: call + all: + - has: + stopBy: neighbor + kind: scope_resolution + regex: ^PG::Connection$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^connect_start|new$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: string + nthChild: 7 + has: + stopBy: neighbor + kind: string_content + PG::Connection.new($HOST, $PORT, $OPS, $TTY, $DB, $USER, "")_with_instance: + # PG::Connection.connect_start($HOST, $PORT, $OPS, $TTY, $DB, $USER,"", ...) + kind: call + all: + - has: + stopBy: neighbor + kind: scope_resolution + regex: ^PG::Connection$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^connect_start|new$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: neighbor + kind: identifier + nthChild: 7 + pattern: $PASS + - inside: + stopBy: end + kind: program + has: + stopBy: end + kind: assignment + pattern: $PASS = '$$$' + PG::Connection.new(password:""): + # PG::Connection.new(..., password: '', ...) + kind: call + all: + - has: + stopBy: neighbor + kind: scope_resolution + regex: ^PG::Connection$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^new|connect_start$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: end + kind: pair + all: + - has: + stopBy: neighbor + regex: ^password|:password$ + - has: + stopBy: neighbor + kind: string + has: + stopBy: neighbor + kind: string_content +rule: + kind: call + any: + - matches: PG.connect(password:"") + - matches: PG.connect(password:"")_with_instance + - matches: PG.connect($HOST, $PORT, $OPS, $TTY, $DB, $USER, "") + - matches: PG.connect($HOST, $PORT, $OPS, $TTY, $DB, $USER, "")_with_instance + - matches: PG::Connection.new(password:"")_with_instance + - matches: PG::Connection.new($HOST, $PORT, $OPS, $TTY, $DB, $USER, "") + - matches: PG::Connection.new($HOST, $PORT, $OPS, $TTY, $DB, $USER, "")_with_instance + - matches: PG::Connection.new(password:"") diff --git a/tests/__snapshots__/ruby-pg-hardcoded-secret-ruby-snapshot.yml b/tests/__snapshots__/ruby-pg-hardcoded-secret-ruby-snapshot.yml new file mode 100644 index 00000000..b2540aa8 --- /dev/null +++ b/tests/__snapshots__/ruby-pg-hardcoded-secret-ruby-snapshot.yml @@ -0,0 +1,325 @@ +id: ruby-pg-hardcoded-secret-ruby +snapshots: + ? | + PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => 'password', + :sslmode => 'prefer' + ) + : labels: + - source: |- + PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => 'password', + :sslmode => 'prefer' + ) + style: primary + start: 0 + end: 147 + - source: PG + style: secondary + start: 0 + end: 2 + - source: . + style: secondary + start: 2 + end: 3 + - source: connect + style: secondary + start: 3 + end: 10 + - source: :password + style: secondary + start: 98 + end: 107 + - source: password + style: secondary + start: 112 + end: 120 + - source: '''password''' + style: secondary + start: 111 + end: 121 + - source: :password => 'password' + style: secondary + start: 98 + end: 121 + - source: |- + ( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => 'password', + :sslmode => 'prefer' + ) + style: secondary + start: 10 + end: 147 + ? | + con3 = PG::.connect nil, 5432, nil, nil, 'test', 'user', 'password' ) + : labels: + - source: PG::.connect nil, 5432, nil, nil, 'test', 'user', 'password' + style: primary + start: 7 + end: 67 + - source: PG + style: secondary + start: 7 + end: 9 + - source: . + style: secondary + start: 11 + end: 12 + - source: connect + style: secondary + start: 12 + end: 19 + - source: password + style: secondary + start: 58 + end: 66 + - source: '''password''' + style: secondary + start: 57 + end: 67 + - source: nil, 5432, nil, nil, 'test', 'user', 'password' + style: secondary + start: 20 + end: 67 + ? | + con3 = PG::Connection.connect_start( nil, 5432, nil, nil, 'test', 'user', 'password' ) + : labels: + - source: PG::Connection.connect_start( nil, 5432, nil, nil, 'test', 'user', 'password' ) + style: primary + start: 7 + end: 86 + - source: PG::Connection + style: secondary + start: 7 + end: 21 + - source: . + style: secondary + start: 21 + end: 22 + - source: connect_start + style: secondary + start: 22 + end: 35 + - source: password + style: secondary + start: 75 + end: 83 + - source: '''password''' + style: secondary + start: 74 + end: 84 + - source: ( nil, 5432, nil, nil, 'test', 'user', 'password' ) + style: secondary + start: 35 + end: 86 + ? | + con3 = PG::Connection.new( nil, 5432, nil, nil, 'test', 'user', 'password' ) + : labels: + - source: PG::Connection.new( nil, 5432, nil, nil, 'test', 'user', 'password' ) + style: primary + start: 7 + end: 76 + - source: PG::Connection + style: secondary + start: 7 + end: 21 + - source: . + style: secondary + start: 21 + end: 22 + - source: new + style: secondary + start: 22 + end: 25 + - source: password + style: secondary + start: 65 + end: 73 + - source: '''password''' + style: secondary + start: 64 + end: 74 + - source: ( nil, 5432, nil, nil, 'test', 'user', 'password' ) + style: secondary + start: 25 + end: 76 + ? | + pw = 'password' + con1 = PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => pw, + :sslmode => 'prefer' + ) + : labels: + - source: |- + PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => pw, + :sslmode => 'prefer' + ) + style: primary + start: 23 + end: 161 + - source: PG + style: secondary + start: 23 + end: 25 + - source: . + style: secondary + start: 25 + end: 26 + - source: connect + style: secondary + start: 26 + end: 33 + - source: :password + style: secondary + start: 121 + end: 130 + - source: pw + style: secondary + start: 134 + end: 136 + - source: :password => pw + style: secondary + start: 121 + end: 136 + - source: |- + ( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => pw, + :sslmode => 'prefer' + ) + style: secondary + start: 33 + end: 161 + - source: pw = 'password' + style: secondary + start: 0 + end: 15 + - source: | + pw = 'password' + con1 = PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => pw, + :sslmode => 'prefer' + ) + style: secondary + start: 0 + end: 162 + ? | + pw = 'password' + con2 = PG::Connection.connect_start( dbname: 'test', port: 5432, user: 'user', password: pw ) + : labels: + - source: 'PG::Connection.connect_start( dbname: ''test'', port: 5432, user: ''user'', password: pw )' + style: primary + start: 23 + end: 109 + - source: PG::Connection + style: secondary + start: 23 + end: 37 + - source: . + style: secondary + start: 37 + end: 38 + - source: connect_start + style: secondary + start: 38 + end: 51 + - source: password + style: secondary + start: 95 + end: 103 + - source: pw + style: secondary + start: 105 + end: 107 + - source: 'password: pw' + style: secondary + start: 95 + end: 107 + - source: '( dbname: ''test'', port: 5432, user: ''user'', password: pw )' + style: secondary + start: 51 + end: 109 + - source: pw = 'password' + style: secondary + start: 0 + end: 15 + - source: | + pw = 'password' + con2 = PG::Connection.connect_start( dbname: 'test', port: 5432, user: 'user', password: pw ) + style: secondary + start: 0 + end: 110 + ? | + pw = 'password' + con2 = PG::Connection.new( dbname: 'test', port: 5432, user: 'user', password: pw ) + : labels: + - source: 'PG::Connection.new( dbname: ''test'', port: 5432, user: ''user'', password: pw )' + style: primary + start: 23 + end: 99 + - source: PG::Connection + style: secondary + start: 23 + end: 37 + - source: . + style: secondary + start: 37 + end: 38 + - source: new + style: secondary + start: 38 + end: 41 + - source: password + style: secondary + start: 85 + end: 93 + - source: pw + style: secondary + start: 95 + end: 97 + - source: 'password: pw' + style: secondary + start: 85 + end: 97 + - source: '( dbname: ''test'', port: 5432, user: ''user'', password: pw )' + style: secondary + start: 41 + end: 99 + - source: pw = 'password' + style: secondary + start: 0 + end: 15 + - source: | + pw = 'password' + con2 = PG::Connection.new( dbname: 'test', port: 5432, user: 'user', password: pw ) + style: secondary + start: 0 + end: 100 diff --git a/tests/ruby/ruby-pg-hardcoded-secret-ruby-test.yml b/tests/ruby/ruby-pg-hardcoded-secret-ruby-test.yml new file mode 100644 index 00000000..e05a9de4 --- /dev/null +++ b/tests/ruby/ruby-pg-hardcoded-secret-ruby-test.yml @@ -0,0 +1,44 @@ +id: ruby-pg-hardcoded-secret-ruby +valid: + - | + con1 = PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => '', + :sslmode => 'prefer' + ) +invalid: + - | + PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => 'password', + :sslmode => 'prefer' + ) + - | + pw = 'password' + con2 = PG::Connection.connect_start( dbname: 'test', port: 5432, user: 'user', password: pw ) + - | + con3 = PG::Connection.new( nil, 5432, nil, nil, 'test', 'user', 'password' ) + - | + con3 = PG::Connection.connect_start( nil, 5432, nil, nil, 'test', 'user', 'password' ) + - | + pw = 'password' + con2 = PG::Connection.new( dbname: 'test', port: 5432, user: 'user', password: pw ) + - | + pw = 'password' + con1 = PG.connect( + :dbname => 'database', + :host => 'host', + :port => 1234, + :user => 'user', + :password => pw, + :sslmode => 'prefer' + ) + - | + con3 = PG::.connect nil, 5432, nil, nil, 'test', 'user', 'password' ) + From a7f5040cd33bfcfc0245fa35eb851091f913f4e4 Mon Sep 17 00:00:00 2001 From: Sakshis Date: Wed, 11 Dec 2024 10:57:48 +0000 Subject: [PATCH 3/3] ruby-redis-empty-secret-ruby --- .../security/ruby-redis-empty-secret-ruby.yml | 104 ++++++++++++ .../ruby-redis-empty-secret-ruby-snapshot.yml | 150 ++++++++++++++++++ .../ruby-redis-empty-secret-ruby-test.yml | 15 ++ 3 files changed, 269 insertions(+) create mode 100644 rules/ruby/security/ruby-redis-empty-secret-ruby.yml create mode 100644 tests/__snapshots__/ruby-redis-empty-secret-ruby-snapshot.yml create mode 100644 tests/ruby/ruby-redis-empty-secret-ruby-test.yml diff --git a/rules/ruby/security/ruby-redis-empty-secret-ruby.yml b/rules/ruby/security/ruby-redis-empty-secret-ruby.yml new file mode 100644 index 00000000..c91f124e --- /dev/null +++ b/rules/ruby/security/ruby-redis-empty-secret-ruby.yml @@ -0,0 +1,104 @@ +id: ruby-redis-empty-secret-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 +utils: + Redis.new(..., password:"", ...): + # Redis.new(..., password: "", ...) + kind: call + all: + - has: + stopBy: neighbor + kind: constant + regex: ^Redis$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^new$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: end + kind: pair + all: + - has: + stopBy: neighbor + kind: hash_key_symbol + regex: ^password$ + - has: + stopBy: neighbor + kind: string + not: + has: + stopBy: neighbor + kind: string_content + - inside: + stopBy: end + kind: program + has: + stopBy: end + kind: call + pattern: require "redis" + Redis.new(..., password:"", ...)_with_instance: + # Redis.new(..., password: $PASS, ...) + kind: call + all: + - has: + stopBy: neighbor + kind: constant + regex: ^Redis$ + - has: + stopBy: neighbor + regex: ^.$ + - has: + stopBy: neighbor + kind: identifier + regex: ^new$ + - has: + stopBy: neighbor + kind: argument_list + has: + stopBy: end + kind: pair + all: + - has: + stopBy: neighbor + kind: hash_key_symbol + regex: ^password$ + - has: + stopBy: neighbor + kind: identifier + pattern: $PASS + - inside: + stopBy: end + kind: assignment + follows: + stopBy: end + kind: assignment + pattern: $PASS = "" + - inside: + stopBy: end + kind: program + has: + stopBy: end + kind: call + pattern: require "redis" +rule: + kind: call + any: + - matches: Redis.new(..., password:"", ...) + - matches: Redis.new(..., password:"", ...)_with_instance diff --git a/tests/__snapshots__/ruby-redis-empty-secret-ruby-snapshot.yml b/tests/__snapshots__/ruby-redis-empty-secret-ruby-snapshot.yml new file mode 100644 index 00000000..b4f2a261 --- /dev/null +++ b/tests/__snapshots__/ruby-redis-empty-secret-ruby-snapshot.yml @@ -0,0 +1,150 @@ +id: ruby-redis-empty-secret-ruby +snapshots: + ? | + require "redis" + pass = '' + redis1 = Redis.new(username: 'myname', password: pass) + : labels: + - source: 'Redis.new(username: ''myname'', password: pass)' + style: primary + start: 35 + end: 80 + - source: Redis + style: secondary + start: 35 + end: 40 + - source: . + style: secondary + start: 40 + end: 41 + - source: new + style: secondary + start: 41 + end: 44 + - source: password + style: secondary + start: 65 + end: 73 + - source: pass + style: secondary + start: 75 + end: 79 + - source: 'password: pass' + style: secondary + start: 65 + end: 79 + - source: '(username: ''myname'', password: pass)' + style: secondary + start: 44 + end: 80 + - source: pass = '' + style: secondary + start: 16 + end: 25 + - source: 'redis1 = Redis.new(username: ''myname'', password: pass)' + style: secondary + start: 26 + end: 80 + - source: require "redis" + style: secondary + start: 0 + end: 15 + - source: | + require "redis" + pass = '' + redis1 = Redis.new(username: 'myname', password: pass) + style: secondary + start: 0 + end: 81 + ? | + require "redis" + redis = Redis.new(password: "") + : labels: + - source: 'Redis.new(password: "")' + style: primary + start: 24 + end: 47 + - source: Redis + style: secondary + start: 24 + end: 29 + - source: . + style: secondary + start: 29 + end: 30 + - source: new + style: secondary + start: 30 + end: 33 + - source: password + style: secondary + start: 34 + end: 42 + - source: '""' + style: secondary + start: 44 + end: 46 + - source: 'password: ""' + style: secondary + start: 34 + end: 46 + - source: '(password: "")' + style: secondary + start: 33 + end: 47 + - source: require "redis" + style: secondary + start: 0 + end: 15 + - source: | + require "redis" + redis = Redis.new(password: "") + style: secondary + start: 0 + end: 48 + ? | + require "redis" + redis1 = Redis.new(username: 'myname', password: '') + : labels: + - source: 'Redis.new(username: ''myname'', password: '''')' + style: primary + start: 25 + end: 68 + - source: Redis + style: secondary + start: 25 + end: 30 + - source: . + style: secondary + start: 30 + end: 31 + - source: new + style: secondary + start: 31 + end: 34 + - source: password + style: secondary + start: 55 + end: 63 + - source: '''''' + style: secondary + start: 65 + end: 67 + - source: 'password: ''''' + style: secondary + start: 55 + end: 67 + - source: '(username: ''myname'', password: '''')' + style: secondary + start: 34 + end: 68 + - source: require "redis" + style: secondary + start: 0 + end: 15 + - source: | + require "redis" + redis1 = Redis.new(username: 'myname', password: '') + style: secondary + start: 0 + end: 69 diff --git a/tests/ruby/ruby-redis-empty-secret-ruby-test.yml b/tests/ruby/ruby-redis-empty-secret-ruby-test.yml new file mode 100644 index 00000000..0eb306ad --- /dev/null +++ b/tests/ruby/ruby-redis-empty-secret-ruby-test.yml @@ -0,0 +1,15 @@ +id: ruby-redis-empty-secret-ruby +valid: + - | + redis_ok1 = Redis.new(username: 'myname', password: ENV["PASS"]) +invalid: + - | + require "redis" + redis = Redis.new(password: "") + - | + require "redis" + redis1 = Redis.new(username: 'myname', password: '') + - | + require "redis" + pass = '' + redis1 = Redis.new(username: 'myname', password: pass)