зеркало из https://github.com/github/codeql.git
Pg Library, change note and Frameworks.qll
This commit is contained in:
Родитель
e33f3a6668
Коммит
ad5355a04a
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Support for the `pg` gem has been added. Method calls that execute queries against an PostgreSQL database that may be vulnerable to injection attacks will now be recognized.
|
|
@ -32,3 +32,4 @@ private import codeql.ruby.frameworks.Slim
|
|||
private import codeql.ruby.frameworks.Sinatra
|
||||
private import codeql.ruby.frameworks.Twirp
|
||||
private import codeql.ruby.frameworks.Sqlite3
|
||||
private import codeql.ruby.frameworks.Pg
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* Provides modeling for Pg, a Ruby library (gem) for interacting with PostgreSQL databases.
|
||||
*/
|
||||
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.dataflow.FlowSummary
|
||||
private import codeql.ruby.Concepts
|
||||
|
||||
/**
|
||||
* Provides modeling for Pg, a Ruby library (gem) for interacting with PostgreSQL databases.
|
||||
*/
|
||||
module Pg {
|
||||
/**
|
||||
* Flow summary for `PG.new()`. This method initializes a database connection.
|
||||
*/
|
||||
private class SqlSummary extends SummarizedCallable {
|
||||
SqlSummary() { this = "PG.new()" }
|
||||
|
||||
override MethodCall getACall() { result = any(PgConnection c).asExpr().getExpr() }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to PG::Connection.open() is used to establish a connection to a PostgreSQL database. */
|
||||
private class PgConnection extends DataFlow::CallNode {
|
||||
PgConnection() {
|
||||
this =
|
||||
API::getTopLevelMember("PG")
|
||||
.getMember("Connection")
|
||||
.getAMethodCall(["open", "new", "connect_start"])
|
||||
or
|
||||
this = API::getTopLevelMember("PG").getAnInstantiation()
|
||||
}
|
||||
}
|
||||
|
||||
/** A call that prepares an SQL statment to be executed later. */
|
||||
private class PgPrepareCall extends SqlConstruction::Range, DataFlow::CallNode {
|
||||
private DataFlow::Node query;
|
||||
private PgConnection pgConnection;
|
||||
private string queryName;
|
||||
|
||||
PgPrepareCall() {
|
||||
this = pgConnection.getAMethodCall("prepare") and
|
||||
queryName = this.getArgument(0).getConstantValue().getStringlikeValue() and
|
||||
query = this.getArgument(1)
|
||||
}
|
||||
|
||||
PgConnection getConnection() { result = pgConnection }
|
||||
|
||||
string getQueryName() { result = queryName }
|
||||
|
||||
override DataFlow::Node getSql() { result = query }
|
||||
}
|
||||
|
||||
/** A call that executes SQL statements against a PostgreSQL database. */
|
||||
private class PgExecution extends SqlExecution::Range, DataFlow::CallNode {
|
||||
private DataFlow::Node query;
|
||||
|
||||
PgExecution() {
|
||||
exists(PgConnection pgConnection |
|
||||
this =
|
||||
pgConnection.getAMethodCall(["exec", "async_exec", "exec_params", "async_exec_params"]) and
|
||||
query = this.getArgument(0)
|
||||
or
|
||||
exists(PgPrepareCall prepareCall |
|
||||
pgConnection = prepareCall.getConnection() and
|
||||
this.getArgument(0).getConstantValue().isStringlikeValue(prepareCall.getQueryName()) and
|
||||
query = prepareCall.getSql()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() { result = query }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
class FooController < ActionController::Base
|
||||
|
||||
def some_request_handler
|
||||
# A string tainted by user input is inserted into a query
|
||||
# (i.e a remote flow source)
|
||||
name = params[:name]
|
||||
|
||||
# Establish a connection to a PostgreSQL database
|
||||
conn = PG::Connection.open(:dbname => 'postgresql', :user => 'user', :password => 'pass', :host => 'localhost', :port => '5432')
|
||||
|
||||
# .exec() and .async_exec()
|
||||
# BAD: SQL statement constructed from user input
|
||||
qry1 = "SELECT * FROM users WHERE username = '#{name}';"
|
||||
conn.exec(qry1)
|
||||
conn.async_exec(qry1)
|
||||
|
||||
# .exec_params() and .async_exec_params()
|
||||
# BAD: SQL statement constructed from user input
|
||||
qry2 = "SELECT * FROM users WHERE username = '#{name}';"
|
||||
conn.exec_params(qry2)
|
||||
conn.async_exec_params(qry2)
|
||||
|
||||
# .exec_params() and .async_exec_params()
|
||||
# GOOD: SQL statement constructed from sanitized user input
|
||||
qry2 = "SELECT * FROM users WHERE username = $1;"
|
||||
conn.exec_params(qry2, [name])
|
||||
conn.async_exec_params(qry2, [name])
|
||||
|
||||
# .prepare() and .exec_prepared()
|
||||
# BAD: SQL statement constructed from user input
|
||||
qry3 = "SELECT * FROM users WHERE username = '#{name}';"
|
||||
conn.prepare("query_1", qry3)
|
||||
conn.exec_prepared('query_1')
|
||||
|
||||
# .prepare() and .exec_prepared()
|
||||
# GOOD: SQL statement constructed from sanitized user input
|
||||
qry3 = "SELECT * FROM users WHERE username = $1;"
|
||||
conn.prepare("query_2", qry3)
|
||||
conn.exec_prepared('query_2', [name])
|
||||
|
||||
# .prepare() and .exec_prepared()
|
||||
# NOT EXECUTED: SQL statement constructed from user input but not executed
|
||||
qry3 = "SELECT * FROM users WHERE username = '#{name}';"
|
||||
conn.prepare("query_3", qry3)
|
||||
end
|
||||
end
|
||||
|
||||
class BarController < ApplicationController
|
||||
def safe_paths
|
||||
name1 = params["name1"]
|
||||
# GOOD: barrier guard prevents taint flow
|
||||
if name == "admin"
|
||||
qry_bar1 = "SELECT * FROM users WHERE username = '%s';" % name
|
||||
else
|
||||
qry_bar1 = "SELECT * FROM users WHERE username = 'none';"
|
||||
end
|
||||
conn.exec_params(qry_bar1)
|
||||
|
||||
|
||||
name2 = params["name2"]
|
||||
# GOOD: barrier guard prevents taint flow
|
||||
name2 = if ["admin", "guest"].include? name2
|
||||
name2
|
||||
else
|
||||
name2 = "none"
|
||||
end
|
||||
qry_bar2 = "SELECT * FROM users WHERE username = '%s';" % name
|
||||
conn.exec_params(qry_bar2)
|
||||
end
|
||||
end
|
Загрузка…
Ссылка в новой задаче