Merge pull request #21341 from owen-mc/rb/accept-mad-sanitizers

Ruby: Accept MaD sanitizers for queries with MaD sinks and convert some existing sanitizers
This commit is contained in:
Owen Mansel-Chan
2026-02-23 11:44:05 +00:00
committed by GitHub
24 changed files with 208 additions and 137 deletions

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* We now track taint flow through `Shellwords.escape` and `Shellwords.shellescape` for all queries except command injection, for which they are sanitizers.

View File

@@ -9,6 +9,7 @@ private import codeql.ruby.CFG
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.internal.DataFlowImplSpecific
private import codeql.ruby.Frameworks
private import codeql.ruby.frameworks.data.internal.ApiGraphModels
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.ApiGraphs
private import codeql.ruby.Regexp as RE
@@ -95,6 +96,10 @@ module SqlSanitization {
abstract class Range extends DataFlow::Node { }
}
private class ExternalSqlInjectionSanitizer extends SqlSanitization::Range {
ExternalSqlInjectionSanitizer() { ModelOutput::barrierNode(this, "sql-injection") }
}
/**
* A data-flow node that executes a regular expression.
*

View File

@@ -0,0 +1,11 @@
extensions:
- addsTo:
pack: codeql/ruby-all
extensible: summaryModel
data:
- ['Mysql2::Client!', 'Method[escape]', 'Argument[0]', 'ReturnValue', 'taint']
- addsTo:
pack: codeql/ruby-all
extensible: barrierModel
data:
- ['Mysql2::Client!', 'Method[escape].ReturnValue', 'sql-injection']

View File

@@ -48,26 +48,4 @@ module Mysql2 {
override DataFlow::Node getSql() { result = query }
}
/**
* A call to `Mysql2::Client.escape`, considered as a sanitizer for SQL statements.
*/
private class Mysql2EscapeSanitization extends SqlSanitization::Range {
Mysql2EscapeSanitization() {
this = API::getTopLevelMember("Mysql2").getMember("Client").getAMethodCall("escape")
}
}
/**
* Flow summary for `Mysql2::Client.escape()`.
*/
private class EscapeSummary extends SummarizedCallable::Range {
EscapeSummary() { this = "Mysql2::Client.escape()" }
override MethodCall getACall() { result = any(Mysql2EscapeSanitization c).asExpr().getExpr() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}
}

View File

@@ -0,0 +1,11 @@
extensions:
- addsTo:
pack: codeql/ruby-all
extensible: summaryModel
data:
- ['SQLite3::Database!', 'Method[quote]', 'Argument[0]', 'ReturnValue', 'taint']
- addsTo:
pack: codeql/ruby-all
extensible: barrierModel
data:
- ['SQLite3::Database!', 'Method[quote].ReturnValue', 'sql-injection']

View File

@@ -76,26 +76,4 @@ module Sqlite3 {
override DataFlow::Node getSql() { result = this.getArgument(0) }
}
/**
* A call to `SQLite3::Database.quote`, considered as a sanitizer for SQL statements.
*/
private class SQLite3QuoteSanitization extends SqlSanitization {
SQLite3QuoteSanitization() {
this = API::getTopLevelMember("SQLite3").getMember("Database").getAMethodCall("quote")
}
}
/**
* Flow summary for `SQLite3::Database.quote()`.
*/
private class QuoteSummary extends SummarizedCallable::Range {
QuoteSummary() { this = "SQLite3::Database.quote()" }
override MethodCall getACall() { result = any(SQLite3QuoteSanitization c).asExpr().getExpr() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}
}

View File

@@ -0,0 +1,12 @@
extensions:
- addsTo:
pack: codeql/ruby-all
extensible: summaryModel
data:
- ['Shellwords!', 'Method[escape,shellescape]', 'Argument[0]', 'ReturnValue', 'taint']
- addsTo:
pack: codeql/ruby-all
extensible: barrierModel
data:
- ['Shellwords!', 'Method[escape,shellescape].ReturnValue', 'command-injection']

View File

@@ -118,4 +118,8 @@ module CodeInjection {
private class ExternalCodeInjectionSink extends Sink {
ExternalCodeInjectionSink() { ModelOutput::sinkNode(this, "code-injection") }
}
private class ExternalCodeInjectionSanitizer extends Sanitizer {
ExternalCodeInjectionSanitizer() { ModelOutput::barrierNode(this, "code-injection") }
}
}

View File

@@ -43,13 +43,11 @@ module CommandInjection {
}
/**
* A call to `Shellwords.escape` or `Shellwords.shellescape` sanitizes its input.
* A call to `String#shellescape` sanitizes its input.
*/
class ShellwordsEscapeAsSanitizer extends Sanitizer {
ShellwordsEscapeAsSanitizer() {
this = API::getTopLevelMember("Shellwords").getAMethodCall(["escape", "shellescape"])
or
// The method is also added as `String#shellescape`.
// The `Shellwords.shellescape` method is also added as `String#shellescape`.
this.(DataFlow::CallNode).getMethodName() = "shellescape"
}
}
@@ -57,4 +55,8 @@ module CommandInjection {
private class ExternalCommandInjectionSink extends Sink {
ExternalCommandInjectionSink() { ModelOutput::sinkNode(this, "command-injection") }
}
private class ExternalCommandInjectionSanitizer extends Sanitizer {
ExternalCommandInjectionSanitizer() { ModelOutput::barrierNode(this, "command-injection") }
}
}

View File

@@ -67,6 +67,10 @@ class HtmlEscapingAsSanitizer extends Sanitizer {
HtmlEscapingAsSanitizer() { this = any(HtmlEscaping esc).getOutput() }
}
private class ExternalLogInjectionSanitizer extends Sanitizer {
ExternalLogInjectionSanitizer() { ModelOutput::barrierNode(this, "log-injection") }
}
private module LogInjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }

View File

@@ -57,4 +57,8 @@ module PathInjection {
private class ExternalPathInjectionSink extends Sink {
ExternalPathInjectionSink() { ModelOutput::sinkNode(this, "path-injection") }
}
private class ExternalPathInjectionSanitizer extends Sanitizer {
ExternalPathInjectionSanitizer() { ModelOutput::barrierNode(this, "path-injection") }
}
}

View File

@@ -46,4 +46,8 @@ module ServerSideRequestForgery {
private class ExternalRequestForgerySink extends Sink {
ExternalRequestForgerySink() { ModelOutput::sinkNode(this, "request-forgery") }
}
private class ExternalRequestForgerySanitizer extends Sanitizer {
ExternalRequestForgerySanitizer() { ModelOutput::barrierNode(this, "request-forgery") }
}
}

View File

@@ -125,6 +125,10 @@ module UrlRedirect {
*/
class StringInterpolationAsSanitizer extends PrefixedStringInterpolation, Sanitizer { }
private class ExternalUrlRedirectSanitizer extends Sanitizer {
ExternalUrlRedirectSanitizer() { ModelOutput::barrierNode(this, "url-redirection") }
}
/**
* These methods return a new `ActionController::Parameters` or a `Hash` containing a subset of
* the original values. This may still contain user input, so the results are tainted.

View File

@@ -2921,13 +2921,11 @@
| file://:0:0:0:0 | [summary param] position 0 in File.realdirpath | file://:0:0:0:0 | [summary] to write: ReturnValue in File.realdirpath |
| file://:0:0:0:0 | [summary param] position 0 in File.realpath | file://:0:0:0:0 | [summary] to write: ReturnValue in File.realpath |
| file://:0:0:0:0 | [summary param] position 0 in Hash[] | file://:0:0:0:0 | [summary] read: Argument[0].Element[any] in Hash[] |
| file://:0:0:0:0 | [summary param] position 0 in Mysql2::Client.escape() | file://:0:0:0:0 | [summary] to write: ReturnValue in Mysql2::Client.escape() |
| file://:0:0:0:0 | [summary param] position 0 in Mysql2::Client.new() | file://:0:0:0:0 | [summary] to write: ReturnValue in Mysql2::Client.new() |
| file://:0:0:0:0 | [summary param] position 0 in Net::LDAP.new | file://:0:0:0:0 | [summary] to write: ReturnValue in Net::LDAP.new |
| file://:0:0:0:0 | [summary param] position 0 in Net::LDAP::Filter | file://:0:0:0:0 | [summary] to write: ReturnValue in Net::LDAP::Filter |
| file://:0:0:0:0 | [summary param] position 0 in PG.new() | file://:0:0:0:0 | [summary] to write: ReturnValue in PG.new() |
| file://:0:0:0:0 | [summary param] position 0 in Rack::Utils.parse_query | file://:0:0:0:0 | [summary] to write: ReturnValue in Rack::Utils.parse_query |
| file://:0:0:0:0 | [summary param] position 0 in SQLite3::Database.quote() | file://:0:0:0:0 | [summary] to write: ReturnValue in SQLite3::Database.quote() |
| file://:0:0:0:0 | [summary param] position 0 in Sequel.connect | file://:0:0:0:0 | [summary] to write: ReturnValue in Sequel.connect |
| file://:0:0:0:0 | [summary param] position 0 in String.try_convert | file://:0:0:0:0 | [summary] to write: ReturnValue in String.try_convert |
| file://:0:0:0:0 | [summary param] position 0 in \| | file://:0:0:0:0 | [summary] read: Argument[0].Element[any] in \| |

View File

@@ -1,6 +1,6 @@
class UsersController < ActionController::Base
def mysql2_handler(event:, context:)
name = params[:user_name]
name = params[:user_name] # $ Source[rb/sql-injection]
conn = Mysql2::Client.new(
host: "127.0.0.1",
@@ -10,7 +10,7 @@ class UsersController < ActionController::Base
results1 = conn.query("SELECT * FROM users")
# BAD: SQL statement constructed from user input
results2 = conn.query("SELECT * FROM users WHERE username='#{name}'")
results2 = conn.query("SELECT * FROM users WHERE username='#{name}'") # $ Alert[rb/sql-injection]
# GOOD: user input is escaped
escaped = Mysql2::Client.escape(name)
@@ -21,10 +21,10 @@ class UsersController < ActionController::Base
results4 = statement1.execute(1, name, :as => :array)
# BAD: SQL statement constructed from user input
statement2 = conn.prepare("SELECT * FROM users WHERE username='#{name}' AND password = ?")
statement2 = conn.prepare("SELECT * FROM users WHERE username='#{name}' AND password = ?") # $ Alert[rb/sql-injection]
results4 = statement2.execute("password", :as => :array)
# NOT EXECUTED
statement3 = conn.prepare("SELECT * FROM users WHERE username = ?")
end
end
end

View File

@@ -0,0 +1,15 @@
#select
| Mysql2.rb:13:27:13:72 | "SELECT * FROM users WHERE use..." | Mysql2.rb:3:12:3:17 | call to params | Mysql2.rb:13:27:13:72 | "SELECT * FROM users WHERE use..." | This SQL query depends on a $@. | Mysql2.rb:3:12:3:17 | call to params | user-provided value |
| Mysql2.rb:24:31:24:93 | "SELECT * FROM users WHERE use..." | Mysql2.rb:3:12:3:17 | call to params | Mysql2.rb:24:31:24:93 | "SELECT * FROM users WHERE use..." | This SQL query depends on a $@. | Mysql2.rb:3:12:3:17 | call to params | user-provided value |
edges
| Mysql2.rb:3:5:3:8 | name | Mysql2.rb:13:27:13:72 | "SELECT * FROM users WHERE use..." | provenance | AdditionalTaintStep |
| Mysql2.rb:3:5:3:8 | name | Mysql2.rb:24:31:24:93 | "SELECT * FROM users WHERE use..." | provenance | AdditionalTaintStep |
| Mysql2.rb:3:12:3:17 | call to params | Mysql2.rb:3:12:3:29 | ...[...] | provenance | |
| Mysql2.rb:3:12:3:29 | ...[...] | Mysql2.rb:3:5:3:8 | name | provenance | |
nodes
| Mysql2.rb:3:5:3:8 | name | semmle.label | name |
| Mysql2.rb:3:12:3:17 | call to params | semmle.label | call to params |
| Mysql2.rb:3:12:3:29 | ...[...] | semmle.label | ...[...] |
| Mysql2.rb:13:27:13:72 | "SELECT * FROM users WHERE use..." | semmle.label | "SELECT * FROM users WHERE use..." |
| Mysql2.rb:24:31:24:93 | "SELECT * FROM users WHERE use..." | semmle.label | "SELECT * FROM users WHERE use..." |
subpaths

View File

@@ -0,0 +1,4 @@
query: queries/security/cwe-089/SqlInjection.ql
postprocess:
- utils/test/PrettyPrintModels.ql
- utils/test/InlineExpectationsTestQuery.ql

View File

@@ -0,0 +1,12 @@
#select
| sqlite3.rb:29:16:29:67 | "select * from table where cat..." | sqlite3.rb:25:16:25:21 | call to params | sqlite3.rb:29:16:29:67 | "select * from table where cat..." | This SQL query depends on a $@. | sqlite3.rb:25:16:25:21 | call to params | user-provided value |
edges
| sqlite3.rb:25:5:25:12 | category | sqlite3.rb:29:16:29:67 | "select * from table where cat..." | provenance | AdditionalTaintStep |
| sqlite3.rb:25:16:25:21 | call to params | sqlite3.rb:25:16:25:32 | ...[...] | provenance | |
| sqlite3.rb:25:16:25:32 | ...[...] | sqlite3.rb:25:5:25:12 | category | provenance | |
nodes
| sqlite3.rb:25:5:25:12 | category | semmle.label | category |
| sqlite3.rb:25:16:25:21 | call to params | semmle.label | call to params |
| sqlite3.rb:25:16:25:32 | ...[...] | semmle.label | ...[...] |
| sqlite3.rb:29:16:29:67 | "select * from table where cat..." | semmle.label | "select * from table where cat..." |
subpaths

View File

@@ -0,0 +1,4 @@
query: queries/security/cwe-089/SqlInjection.ql
postprocess:
- utils/test/PrettyPrintModels.ql
- utils/test/InlineExpectationsTestQuery.ql

View File

@@ -2,9 +2,11 @@ sqlite3SqlConstruction
| sqlite3.rb:5:1:5:17 | call to execute | sqlite3.rb:5:12:5:17 | <<-SQL |
| sqlite3.rb:12:8:12:41 | call to prepare | sqlite3.rb:12:19:12:41 | "select * from numbers" |
| sqlite3.rb:17:3:19:5 | call to execute | sqlite3.rb:17:15:17:35 | "select * from table" |
| sqlite3.rb:29:7:29:40 | call to execute | sqlite3.rb:29:19:29:39 | "select * from table" |
| sqlite3.rb:29:5:29:68 | call to execute | sqlite3.rb:29:16:29:67 | "select * from table where cat..." |
| sqlite3.rb:33:5:33:78 | call to execute | sqlite3.rb:33:16:33:77 | "select * from table where cat..." |
sqlite3SqlExecution
| sqlite3.rb:5:1:5:17 | call to execute | sqlite3.rb:5:12:5:17 | <<-SQL |
| sqlite3.rb:14:1:14:12 | call to execute | sqlite3.rb:12:19:12:41 | "select * from numbers" |
| sqlite3.rb:17:3:19:5 | call to execute | sqlite3.rb:17:15:17:35 | "select * from table" |
| sqlite3.rb:29:7:29:40 | call to execute | sqlite3.rb:29:19:29:39 | "select * from table" |
| sqlite3.rb:29:5:29:68 | call to execute | sqlite3.rb:29:16:29:67 | "select * from table where cat..." |
| sqlite3.rb:33:5:33:78 | call to execute | sqlite3.rb:33:16:33:77 | "select * from table where cat..." |

View File

@@ -20,12 +20,16 @@ SQLite3::Database.new( "data.db" ) do |db|
end
class MyDatabaseWrapper
def initialize(filename)
@db = SQLite3::Database.new(filename, results_as_hash: true)
end
class SqliteController < ActionController::Base
def sqlite3_handler
category = params[:category] # $ Source[rb/sql-injection]
db = SQLite3::Database.new "test.db"
def select_rows(category)
@db.execute("select * from table")
end
# BAD: SQL injection vulnerability
db.execute("select * from table where category = '#{category}'") # $ Alert[rb/sql-injection]
# GOOD: Sanitized by SQLite3::Database.quote
sanitized_category = SQLite3::Database.quote(category)
db.execute("select * from table where category = '#{sanitized_category}'")
end
end

View File

@@ -1,3 +1,46 @@
#select
| CodeInjection.rb:8:10:8:13 | code | CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:8:10:8:13 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:11:10:11:15 | call to params | CodeInjection.rb:11:10:11:15 | call to params | CodeInjection.rb:11:10:11:15 | call to params | This code execution depends on a $@. | CodeInjection.rb:11:10:11:15 | call to params | user-provided value |
| CodeInjection.rb:20:20:20:23 | code | CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:20:20:20:23 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:23:21:23:24 | code | CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:23:21:23:24 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:29:15:29:18 | code | CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:29:15:29:18 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:32:19:32:22 | code | CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:32:19:32:22 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:38:10:38:28 | call to escape | CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:38:10:38:28 | call to escape | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:41:40:41:43 | code | CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:41:40:41:43 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:81:16:81:19 | code | CodeInjection.rb:78:12:78:17 | call to params | CodeInjection.rb:81:16:81:19 | code | This code execution depends on a $@. | CodeInjection.rb:78:12:78:17 | call to params | user-provided value |
| CodeInjection.rb:90:10:90:37 | ... + ... | CodeInjection.rb:78:12:78:17 | call to params | CodeInjection.rb:90:10:90:37 | ... + ... | This code execution depends on a $@. | CodeInjection.rb:78:12:78:17 | call to params | user-provided value |
| CodeInjection.rb:93:10:93:32 | "prefix_#{...}_suffix" | CodeInjection.rb:78:12:78:17 | call to params | CodeInjection.rb:93:10:93:32 | "prefix_#{...}_suffix" | This code execution depends on a $@. | CodeInjection.rb:78:12:78:17 | call to params | user-provided value |
| CodeInjection.rb:96:10:96:13 | code | CodeInjection.rb:78:12:78:17 | call to params | CodeInjection.rb:96:10:96:13 | code | This code execution depends on a $@. | CodeInjection.rb:78:12:78:17 | call to params | user-provided value |
| CodeInjection.rb:118:10:118:13 | @foo | CodeInjection.rb:111:12:111:17 | call to params | CodeInjection.rb:118:10:118:13 | @foo | This code execution depends on a $@. | CodeInjection.rb:111:12:111:17 | call to params | user-provided value |
edges
| CodeInjection.rb:5:5:5:8 | code | CodeInjection.rb:8:10:8:13 | code | provenance | |
| CodeInjection.rb:5:5:5:8 | code | CodeInjection.rb:20:20:20:23 | code | provenance | |
| CodeInjection.rb:5:5:5:8 | code | CodeInjection.rb:23:21:23:24 | code | provenance | |
| CodeInjection.rb:5:5:5:8 | code | CodeInjection.rb:29:15:29:18 | code | provenance | |
| CodeInjection.rb:5:5:5:8 | code | CodeInjection.rb:32:19:32:22 | code | provenance | |
| CodeInjection.rb:5:5:5:8 | code | CodeInjection.rb:38:24:38:27 | code | provenance | |
| CodeInjection.rb:5:5:5:8 | code | CodeInjection.rb:41:40:41:43 | code | provenance | |
| CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:5:12:5:24 | ...[...] | provenance | |
| CodeInjection.rb:5:12:5:24 | ...[...] | CodeInjection.rb:5:5:5:8 | code | provenance | |
| CodeInjection.rb:38:24:38:27 | code | CodeInjection.rb:38:10:38:28 | call to escape | provenance | MaD:1 |
| CodeInjection.rb:78:5:78:8 | code | CodeInjection.rb:81:16:81:19 | code | provenance | |
| CodeInjection.rb:78:5:78:8 | code | CodeInjection.rb:90:10:90:37 | ... + ... | provenance | |
| CodeInjection.rb:78:5:78:8 | code | CodeInjection.rb:90:22:90:25 | code | provenance | |
| CodeInjection.rb:78:5:78:8 | code | CodeInjection.rb:93:10:93:32 | "prefix_#{...}_suffix" | provenance | AdditionalTaintStep |
| CodeInjection.rb:78:5:78:8 | code | CodeInjection.rb:96:10:96:13 | code | provenance | |
| CodeInjection.rb:78:12:78:17 | call to params | CodeInjection.rb:78:12:78:24 | ...[...] | provenance | |
| CodeInjection.rb:78:12:78:24 | ...[...] | CodeInjection.rb:78:5:78:8 | code | provenance | |
| CodeInjection.rb:90:10:90:25 | ... + ... : [collection] [element] | CodeInjection.rb:90:10:90:37 | ... + ... | provenance | |
| CodeInjection.rb:90:22:90:25 | code | CodeInjection.rb:90:10:90:25 | ... + ... : [collection] [element] | provenance | |
| CodeInjection.rb:107:3:108:5 | self in index : PostsController [@foo] | CodeInjection.rb:117:3:119:5 | self in baz : PostsController [@foo] | provenance | |
| CodeInjection.rb:111:5:111:8 | [post] self [@foo] | CodeInjection.rb:114:3:115:5 | self in bar : PostsController [@foo] | provenance | |
| CodeInjection.rb:111:12:111:17 | call to params | CodeInjection.rb:111:12:111:23 | ...[...] | provenance | |
| CodeInjection.rb:111:12:111:23 | ...[...] | CodeInjection.rb:111:5:111:8 | [post] self [@foo] | provenance | |
| CodeInjection.rb:114:3:115:5 | self in bar : PostsController [@foo] | CodeInjection.rb:107:3:108:5 | self in index : PostsController [@foo] | provenance | |
| CodeInjection.rb:117:3:119:5 | self in baz : PostsController [@foo] | CodeInjection.rb:118:10:118:13 | self : PostsController [@foo] | provenance | |
| CodeInjection.rb:118:10:118:13 | self : PostsController [@foo] | CodeInjection.rb:118:10:118:13 | @foo | provenance | |
models
| 1 | Summary: Regexp!; Method[escape,quote]; Argument[0]; ReturnValue; taint |
nodes
| CodeInjection.rb:5:5:5:8 | code | semmle.label | code |
| CodeInjection.rb:5:12:5:17 | call to params | semmle.label | call to params |
@@ -14,59 +57,18 @@ nodes
| CodeInjection.rb:78:5:78:8 | code | semmle.label | code |
| CodeInjection.rb:78:12:78:17 | call to params | semmle.label | call to params |
| CodeInjection.rb:78:12:78:24 | ...[...] | semmle.label | ...[...] |
| CodeInjection.rb:80:16:80:19 | code | semmle.label | code |
| CodeInjection.rb:86:10:86:25 | ... + ... : [collection] [element] | semmle.label | ... + ... : [collection] [element] |
| CodeInjection.rb:86:10:86:37 | ... + ... | semmle.label | ... + ... |
| CodeInjection.rb:86:22:86:25 | code | semmle.label | code |
| CodeInjection.rb:88:10:88:32 | "prefix_#{...}_suffix" | semmle.label | "prefix_#{...}_suffix" |
| CodeInjection.rb:90:10:90:13 | code | semmle.label | code |
| CodeInjection.rb:101:3:102:5 | self in index : PostsController [@foo] | semmle.label | self in index : PostsController [@foo] |
| CodeInjection.rb:105:5:105:8 | [post] self [@foo] | semmle.label | [post] self [@foo] |
| CodeInjection.rb:105:12:105:17 | call to params | semmle.label | call to params |
| CodeInjection.rb:105:12:105:23 | ...[...] | semmle.label | ...[...] |
| CodeInjection.rb:108:3:109:5 | self in bar : PostsController [@foo] | semmle.label | self in bar : PostsController [@foo] |
| CodeInjection.rb:111:3:113:5 | self in baz : PostsController [@foo] | semmle.label | self in baz : PostsController [@foo] |
| CodeInjection.rb:112:10:112:13 | @foo | semmle.label | @foo |
| CodeInjection.rb:112:10:112:13 | self : PostsController [@foo] | semmle.label | self : PostsController [@foo] |
edges
| CodeInjection.rb:5:5:5:8 | code | CodeInjection.rb:8:10:8:13 | code | provenance | |
| CodeInjection.rb:5:5:5:8 | code | CodeInjection.rb:20:20:20:23 | code | provenance | |
| CodeInjection.rb:5:5:5:8 | code | CodeInjection.rb:23:21:23:24 | code | provenance | |
| CodeInjection.rb:5:5:5:8 | code | CodeInjection.rb:29:15:29:18 | code | provenance | |
| CodeInjection.rb:5:5:5:8 | code | CodeInjection.rb:32:19:32:22 | code | provenance | |
| CodeInjection.rb:5:5:5:8 | code | CodeInjection.rb:38:24:38:27 | code | provenance | |
| CodeInjection.rb:5:5:5:8 | code | CodeInjection.rb:41:40:41:43 | code | provenance | |
| CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:5:12:5:24 | ...[...] | provenance | |
| CodeInjection.rb:5:12:5:24 | ...[...] | CodeInjection.rb:5:5:5:8 | code | provenance | |
| CodeInjection.rb:38:24:38:27 | code | CodeInjection.rb:38:10:38:28 | call to escape | provenance | MaD:21 |
| CodeInjection.rb:78:5:78:8 | code | CodeInjection.rb:80:16:80:19 | code | provenance | |
| CodeInjection.rb:78:5:78:8 | code | CodeInjection.rb:86:10:86:37 | ... + ... | provenance | |
| CodeInjection.rb:78:5:78:8 | code | CodeInjection.rb:86:22:86:25 | code | provenance | |
| CodeInjection.rb:78:5:78:8 | code | CodeInjection.rb:88:10:88:32 | "prefix_#{...}_suffix" | provenance | AdditionalTaintStep |
| CodeInjection.rb:78:5:78:8 | code | CodeInjection.rb:90:10:90:13 | code | provenance | |
| CodeInjection.rb:78:12:78:17 | call to params | CodeInjection.rb:78:12:78:24 | ...[...] | provenance | |
| CodeInjection.rb:78:12:78:24 | ...[...] | CodeInjection.rb:78:5:78:8 | code | provenance | |
| CodeInjection.rb:86:10:86:25 | ... + ... : [collection] [element] | CodeInjection.rb:86:10:86:37 | ... + ... | provenance | |
| CodeInjection.rb:86:22:86:25 | code | CodeInjection.rb:86:10:86:25 | ... + ... : [collection] [element] | provenance | |
| CodeInjection.rb:101:3:102:5 | self in index : PostsController [@foo] | CodeInjection.rb:111:3:113:5 | self in baz : PostsController [@foo] | provenance | |
| CodeInjection.rb:105:5:105:8 | [post] self [@foo] | CodeInjection.rb:108:3:109:5 | self in bar : PostsController [@foo] | provenance | |
| CodeInjection.rb:105:12:105:17 | call to params | CodeInjection.rb:105:12:105:23 | ...[...] | provenance | |
| CodeInjection.rb:105:12:105:23 | ...[...] | CodeInjection.rb:105:5:105:8 | [post] self [@foo] | provenance | |
| CodeInjection.rb:108:3:109:5 | self in bar : PostsController [@foo] | CodeInjection.rb:101:3:102:5 | self in index : PostsController [@foo] | provenance | |
| CodeInjection.rb:111:3:113:5 | self in baz : PostsController [@foo] | CodeInjection.rb:112:10:112:13 | self : PostsController [@foo] | provenance | |
| CodeInjection.rb:112:10:112:13 | self : PostsController [@foo] | CodeInjection.rb:112:10:112:13 | @foo | provenance | |
| CodeInjection.rb:81:16:81:19 | code | semmle.label | code |
| CodeInjection.rb:90:10:90:25 | ... + ... : [collection] [element] | semmle.label | ... + ... : [collection] [element] |
| CodeInjection.rb:90:10:90:37 | ... + ... | semmle.label | ... + ... |
| CodeInjection.rb:90:22:90:25 | code | semmle.label | code |
| CodeInjection.rb:93:10:93:32 | "prefix_#{...}_suffix" | semmle.label | "prefix_#{...}_suffix" |
| CodeInjection.rb:96:10:96:13 | code | semmle.label | code |
| CodeInjection.rb:107:3:108:5 | self in index : PostsController [@foo] | semmle.label | self in index : PostsController [@foo] |
| CodeInjection.rb:111:5:111:8 | [post] self [@foo] | semmle.label | [post] self [@foo] |
| CodeInjection.rb:111:12:111:17 | call to params | semmle.label | call to params |
| CodeInjection.rb:111:12:111:23 | ...[...] | semmle.label | ...[...] |
| CodeInjection.rb:114:3:115:5 | self in bar : PostsController [@foo] | semmle.label | self in bar : PostsController [@foo] |
| CodeInjection.rb:117:3:119:5 | self in baz : PostsController [@foo] | semmle.label | self in baz : PostsController [@foo] |
| CodeInjection.rb:118:10:118:13 | @foo | semmle.label | @foo |
| CodeInjection.rb:118:10:118:13 | self : PostsController [@foo] | semmle.label | self : PostsController [@foo] |
subpaths
#select
| CodeInjection.rb:8:10:8:13 | code | CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:8:10:8:13 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:11:10:11:15 | call to params | CodeInjection.rb:11:10:11:15 | call to params | CodeInjection.rb:11:10:11:15 | call to params | This code execution depends on a $@. | CodeInjection.rb:11:10:11:15 | call to params | user-provided value |
| CodeInjection.rb:20:20:20:23 | code | CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:20:20:20:23 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:23:21:23:24 | code | CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:23:21:23:24 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:29:15:29:18 | code | CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:29:15:29:18 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:32:19:32:22 | code | CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:32:19:32:22 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:38:10:38:28 | call to escape | CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:38:10:38:28 | call to escape | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:41:40:41:43 | code | CodeInjection.rb:5:12:5:17 | call to params | CodeInjection.rb:41:40:41:43 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:80:16:80:19 | code | CodeInjection.rb:78:12:78:17 | call to params | CodeInjection.rb:80:16:80:19 | code | This code execution depends on a $@. | CodeInjection.rb:78:12:78:17 | call to params | user-provided value |
| CodeInjection.rb:86:10:86:37 | ... + ... | CodeInjection.rb:78:12:78:17 | call to params | CodeInjection.rb:86:10:86:37 | ... + ... | This code execution depends on a $@. | CodeInjection.rb:78:12:78:17 | call to params | user-provided value |
| CodeInjection.rb:88:10:88:32 | "prefix_#{...}_suffix" | CodeInjection.rb:78:12:78:17 | call to params | CodeInjection.rb:88:10:88:32 | "prefix_#{...}_suffix" | This code execution depends on a $@. | CodeInjection.rb:78:12:78:17 | call to params | user-provided value |
| CodeInjection.rb:90:10:90:13 | code | CodeInjection.rb:78:12:78:17 | call to params | CodeInjection.rb:90:10:90:13 | code | This code execution depends on a $@. | CodeInjection.rb:78:12:78:17 | call to params | user-provided value |
| CodeInjection.rb:112:10:112:13 | @foo | CodeInjection.rb:105:12:105:17 | call to params | CodeInjection.rb:112:10:112:13 | @foo | This code execution depends on a $@. | CodeInjection.rb:105:12:105:17 | call to params | user-provided value |

View File

@@ -1 +1,4 @@
queries/security/cwe-094/CodeInjection.ql
query: queries/security/cwe-094/CodeInjection.ql
postprocess:
- utils/test/PrettyPrintModels.ql
- utils/test/InlineExpectationsTestQuery.ql

View File

@@ -2,13 +2,13 @@ require 'active_job'
class UsersController < ActionController::Base
def create
code = params[:code]
code = params[:code] # $ Source
# BAD
eval(code)
eval(code) # $ Alert
# BAD
eval(params)
eval(params) # $ Alert
# GOOD - user input is in second argument, which is not evaluated as Ruby code
send(:sanitize, params[:code])
@@ -17,28 +17,28 @@ class UsersController < ActionController::Base
Foo.new.bar(code)
# BAD
Foo.class_eval(code)
Foo.class_eval(code) # $ Alert
# BAD
Foo.module_eval(code)
Foo.module_eval(code) # $ Alert
# GOOD
Bar.class_eval(code)
# BAD
const_get(code)
const_get(code) # $ Alert
# BAD
Foo.const_get(code)
Foo.const_get(code) # $ Alert
# GOOD
Bar.const_get(code)
# BAD
eval(Regexp.escape(code))
eval(Regexp.escape(code)) # $ Alert
# BAD
ActiveJob::Serializers.deserialize(code)
ActiveJob::Serializers.deserialize(code) # $ Alert
end
def update
@@ -75,19 +75,25 @@ end
class UsersController < ActionController::Base
def create
code = params[:code]
code = params[:code] # $ Source
obj().send(code, "foo"); # BAD
# BAD
obj().send(code, "foo"); # $ Alert
obj().send("prefix_" + code + "_suffix", "foo"); # GOOD
# GOOD
obj().send("prefix_" + code + "_suffix", "foo");
obj().send("prefix_#{code}_suffix", "foo"); # GOOD
# GOOD
obj().send("prefix_#{code}_suffix", "foo");
eval("prefix_" + code + "_suffix"); # BAD
# BAD
eval("prefix_" + code + "_suffix"); # $ Alert
eval("prefix_#{code}_suffix"); # BAD
# BAD
eval("prefix_#{code}_suffix"); # $ Alert
eval(code); # BAD
# BAD
eval(code); # $ Alert
end
end
@@ -102,13 +108,13 @@ class PostsController < ActionController::Base
end
def foo
@foo = params[:foo]
@foo = params[:foo] # $ Source
end
def bar
end
def baz
eval(@foo)
eval(@foo) # $ Alert
end
end