← 返回首页
Customizing library models for Ruby — CodeQL CodeQL docs
CodeQL documentation
CodeQL resources

Customizing library models for Ruby

Beta Notice - Unstable API

Library customization using data extensions is currently in beta and subject to change.

Breaking changes to this format may occur while in beta.

Ruby analysis can be customized by adding library models in data extension files.

A data extension for Ruby is a YAML file of the form:

extensions: - addsTo: pack: codeql/ruby-all extensible: <name of extensible predicate> data: - <tuple1> - <tuple2> - ...

The CodeQL library for Ruby exposes the following extensible predicates:

We’ll explain how to use these using a few examples, and provide some reference material at the end of this article.

Example: Taint sink in the ‘tty-command’ gem

In this example, we’ll show how to add the following argument, passed to tty-command, as a command-line injection sink:

tty = TTY::Command.new tty.run(cmd) # <-- add 'cmd' as a taint sink

We need to add a tuple to the sinkModel(type, path, kind) extensible predicate by updating a data extension file.

extensions: - addsTo: pack: codeql/ruby-all extensible: sinkModel data: - ["TTY::Command", "Method[run].Argument[0]", "command-injection"]

Example: Taint sources from ‘sinatra’ block parameters

In this example, we’ll show how the ‘x’ parameter below could be marked as a remote flow source:

class MyApp < Sinatra::Base get '/' do |x| # <-- add 'x' as a taint source # ... end end

We need to add a tuple to the sourceModel(type, path, kind) extensible predicate by updating a data extension file.

extensions: - addsTo: pack: codeql/ruby-all extensible: sourceModel data: - [ "Sinatra::Base!", "Method[get].Argument[block].Parameter[0]", "remote", ]

Example: Using types to add MySQL injection sinks

In this example, we’ll show how to add the following SQL injection sink:

def submit(q) client = Mysql2::Client.new client.query(q) # <-- add 'q' as a SQL injection sink end

We need to add a tuple to the sinkModel(type, path, kind) extensible predicate by updating a data extension file.

extensions: - addsTo: pack: codeql/ruby-all extensible: sinkModel data: - ["Mysql2::Client", "Method[query].Argument[0]", "sql-injection"]

Continued example: Using type models

Consider this variation on the previous example, the mysql2 EventMachine API is used. The client is obtained via a call to Mysql2::EM::Client.new.

def submit(client, q) client = Mysql2::EM::Client.new client.query(q) end

So far we have only one model for Mysql2::Client, but in the real world we may have many models for the various methods available. Because Mysql2::EM::Client is a subclass of Mysql2::Client, it inherits all of the same methods. Instead of updating all our models to include both classes, we can add a tuple to the typeModel(type, subtype, ext) extensible predicate to indicate that Mysql2::EM::Client is a subclass of Mysql2::Client:

extensions: - addsTo: pack: codeql/ruby-all extensible: typeModel data: - ["Mysql2::Client", "Mysql2::EM::Client", ""]

Example: Adding flow through ‘URI.decode_uri_component’

In this example, we’ll show how to add flow through calls to ‘URI.decode_uri_component’:

y = URI.decode_uri_component(x); # add taint flow from 'x' to 'y'

We need to add a tuple to the summaryModel(type, path, input, output, kind) extensible predicate by updating a data extension file.

extensions: - addsTo: pack: codeql/ruby-all extensible: summaryModel data: - [ "URI!", "Method[decode_uri_component]", "Argument[0]", "ReturnValue", "taint", ]

Example: Adding flow through ‘File#each’

In this example, we’ll show how to add flow through calls to File#each from the standard library, which iterates over the lines of a file:

f = File.new("example.txt") f.each { |line| ... } # add taint flow from `f` to `line`

We need to add a tuple to the summaryModel(type, path, input, output, kind) extensible predicate by updating a data extension file.

extensions: - addsTo: pack: codeql/ruby-all extensible: summaryModel data: - [ "File", "Method[each]", "Argument[self]", "Argument[block].Parameter[0]", "taint", ]

Example: Taint barrier using the ‘escape’ method

In this example, we’ll show how to add the return value of Mysql2::Client#escape as a barrier for SQL injection.

client = Mysql2::Client.new escaped = client.escape(input) # The return value of this method is safe for SQL injection. client.query("SELECT * FROM users WHERE name = '#{escaped}'")

We need to add a tuple to the barrierModel(type, path, kind) extensible predicate by updating a data extension file.

extensions: - addsTo: pack: codeql/ruby-all extensible: barrierModel data: - ["Mysql2::Client!", "Method[escape].ReturnValue", "sql-injection"]

Example: Add a barrier guard

This example shows how to model a barrier guard that stops the flow of taint when a conditional check is performed on data. Consider a validation method Validator.is_safe which returns true when the data is considered safe.

if Validator.is_safe(user_input) # The check guards the use, so the input is safe. client.query("SELECT * FROM users WHERE name = '#{user_input}'") end

We need to add a tuple to the barrierGuardModel(type, path, acceptingValue, kind) extensible predicate by updating a data extension file.

extensions: - addsTo: pack: codeql/ruby-all extensible: barrierGuardModel data: - ["Validator!", "Method[is_safe].Argument[0]", "true", "sql-injection"]

Reference material

The following sections provide reference material for extensible predicates, access paths, types, and kinds.

Extensible predicates

sourceModel(type, path, kind)

Adds a new taint source. Most taint-tracking queries will use the new source.

Example:

extensions: - addsTo: pack: codeql/ruby-all extensible: sourceModel data: - ["User", "Method[name]", "remote"]

sinkModel(type, path, kind)

Adds a new taint sink. Sinks are query-specific and will typically affect one or two queries.

Example:

extensions: - addsTo: pack: codeql/ruby-all extensible: sinkModel data: - ["ExecuteShell", "Method[run].Argument[0]", "command-injection"]

summaryModel(type, path, input, output, kind)

Adds flow through a method call.

Example:

extensions: - addsTo: pack: codeql/ruby-all extensible: summaryModel data: - [ "URI", "Method[decode_uri_component]", "Argument[0]", "ReturnValue", "taint", ]

typeModel(type1, type2, path)

Adds a new definition of a type.

Example:

extensions: - addsTo: pack: codeql/ruby-all extensible: typeModel data: - [ "Mysql2::Client", "MyDbWrapper", "Method[getConnection].ReturnValue", ]

Types

A type is a string that identifies a set of values. In each of the extensible predicates mentioned in previous section, the first column is always the name of a type. A type can be defined by adding typeModel tuples for that type.

Access paths

The path, input, and output columns consist of a .-separated list of components, which is evaluated from left to right, with each step selecting a new set of values derived from the previous set of values.

The following components are supported:

Additional notes about the syntax of operands:

Kinds

Source kinds

Sink kinds

Unlike sources, sinks tend to be highly query-specific, rarely affecting more than one or two queries. Not every query supports customizable sinks. If the following sinks are not suitable for your use case, you should add a new query.

Summary kinds