Skip to content

Commit 83b3312

Browse files
authored
Merge pull request github#11207 from github/nickrolfe/arel-sql
Ruby: add `SqlConstruction` concept, and implement it for calls to `Arel.sql`
2 parents dd525a4 + be60a87 commit 83b3312

File tree

12 files changed

+161
-21
lines changed

12 files changed

+161
-21
lines changed

python/ql/lib/semmle/python/Concepts.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ module CodeExecution {
311311
* Often, it is worthy of an alert if an SQL statement is constructed such that
312312
* executing it would be a security risk.
313313
*
314-
* If it is important that the SQL statement is indeed executed, then use `SQLExecution`.
314+
* If it is important that the SQL statement is indeed executed, then use `SqlExecution`.
315315
*
316316
* Extend this class to refine existing API models. If you want to model new APIs,
317317
* extend `SqlConstruction::Range` instead.
@@ -329,7 +329,7 @@ module SqlConstruction {
329329
* Often, it is worthy of an alert if an SQL statement is constructed such that
330330
* executing it would be a security risk.
331331
*
332-
* If it is important that the SQL statement is indeed executed, then use `SQLExecution`.
332+
* If it is important that the SQL statement is indeed executed, then use `SqlExecution`.
333333
*
334334
* Extend this class to model new APIs. If you want to refine existing API models,
335335
* extend `SqlConstruction` instead.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The `codeql.ruby.Concepts` library now has a `SqlConstruction` class, in addition to the existing `SqlExecution` class.
5+
* Calls to `Arel.sql` are now modeled as instances of the new `SqlConstruction` concept.

ruby/ql/lib/codeql/ruby/Concepts.qll

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,47 @@ private import codeql.ruby.Frameworks
1111
private import codeql.ruby.dataflow.RemoteFlowSources
1212
private import codeql.ruby.ApiGraphs
1313

14+
/**
15+
* A data-flow node that constructs a SQL statement.
16+
*
17+
* Often, it is worthy of an alert if a SQL statement is constructed such that
18+
* executing it would be a security risk.
19+
*
20+
* If it is important that the SQL statement is executed, use `SqlExecution`.
21+
*
22+
* Extend this class to refine existing API models. If you want to model new APIs,
23+
* extend `SqlConstruction::Range` instead.
24+
*/
25+
class SqlConstruction extends DataFlow::Node instanceof SqlConstruction::Range {
26+
/** Gets the argument that specifies the SQL statements to be constructed. */
27+
DataFlow::Node getSql() { result = super.getSql() }
28+
}
29+
30+
/** Provides a class for modeling new SQL execution APIs. */
31+
module SqlConstruction {
32+
/**
33+
* A data-flow node that constructs a SQL statement.
34+
*
35+
* Often, it is worthy of an alert if a SQL statement is constructed such that
36+
* executing it would be a security risk.
37+
*
38+
* If it is important that the SQL statement is executed, use `SqlExecution`.
39+
*
40+
* Extend this class to model new APIs. If you want to refine existing API models,
41+
* extend `SqlConstruction` instead.
42+
*/
43+
abstract class Range extends DataFlow::Node {
44+
/** Gets the argument that specifies the SQL statements to be constructed. */
45+
abstract DataFlow::Node getSql();
46+
}
47+
}
48+
1449
/**
1550
* A data-flow node that executes SQL statements.
1651
*
52+
* If the context of interest is such that merely constructing a SQL statement
53+
* would be valuable to report, consider using `SqlConstruction`.
54+
*
1755
* Extend this class to refine existing API models. If you want to model new APIs,
1856
* extend `SqlExecution::Range` instead.
1957
*/
@@ -27,6 +65,9 @@ module SqlExecution {
2765
/**
2866
* A data-flow node that executes SQL statements.
2967
*
68+
* If the context of interest is such that merely constructing a SQL
69+
* statement would be valuable to report, consider using `SqlConstruction`.
70+
*
3071
* Extend this class to model new APIs. If you want to refine existing API models,
3172
* extend `SqlExecution` instead.
3273
*/

ruby/ql/lib/codeql/ruby/frameworks/Arel.qll

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
private import codeql.ruby.ApiGraphs
88
private import codeql.ruby.dataflow.FlowSummary
9+
private import codeql.ruby.Concepts
910

1011
/**
1112
* Provides modeling for Arel, a low level SQL library that powers ActiveRecord.
@@ -28,4 +29,14 @@ module Arel {
2829
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
2930
}
3031
}
32+
33+
/** A call to `Arel.sql`, considered as a SQL construction. */
34+
private class ArelSqlConstruction extends SqlConstruction::Range, DataFlow::CallNode {
35+
ArelSqlConstruction() {
36+
this = DataFlow::getConstant("Arel").getAMethodCall() and
37+
this.getMethodName() = "sql"
38+
}
39+
40+
override DataFlow::Node getSql() { result = this.getArgument(0) }
41+
}
3142
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* Provides default sources, sinks and sanitizers for detecting SQL injection
3+
* vulnerabilities, as well as extension points for adding your own.
4+
*/
5+
6+
private import codeql.ruby.Concepts
7+
private import codeql.ruby.DataFlow
8+
private import codeql.ruby.dataflow.BarrierGuards
9+
private import codeql.ruby.dataflow.RemoteFlowSources
10+
11+
/**
12+
* Provides default sources, sinks and sanitizers for detecting SQL injection
13+
* vulnerabilities, as well as extension points for adding your own.
14+
*/
15+
module SqlInjection {
16+
/** A data flow source for SQL injection vulnerabilities. */
17+
abstract class Source extends DataFlow::Node { }
18+
19+
/** A data flow sink for SQL injection vulnerabilities. */
20+
abstract class Sink extends DataFlow::Node { }
21+
22+
/** A sanitizer for SQL injection vulnerabilities. */
23+
abstract class Sanitizer extends DataFlow::Node { }
24+
25+
/**
26+
* A source of remote user input, considered as a flow source.
27+
*/
28+
private class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
29+
30+
/**
31+
* An SQL statement of a SQL execution, considered as a flow sink.
32+
*/
33+
private class SqlExecutionAsSink extends Sink {
34+
SqlExecutionAsSink() { this = any(SqlExecution e).getSql() }
35+
}
36+
37+
/**
38+
* An SQL statement of a SQL construction, considered as a flow sink.
39+
*/
40+
private class SqlConstructionAsSink extends Sink {
41+
SqlConstructionAsSink() { this = any(SqlConstruction e).getSql() }
42+
}
43+
44+
/**
45+
* A comparison with a constant string, considered as a sanitizer-guard.
46+
*/
47+
private class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { }
48+
49+
/**
50+
* An inclusion check against an array of constant strings, considered as a
51+
* sanitizer-guard.
52+
*/
53+
class StringConstArrayInclusionCallAsSanitizer extends Sanitizer,
54+
StringConstArrayInclusionCallBarrier { }
55+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Provides default sources, sinks and sanitizers for detecting SQL injection
3+
* vulnerabilities, as well as extension points for adding your own.
4+
*/
5+
6+
private import codeql.ruby.DataFlow
7+
private import codeql.ruby.TaintTracking
8+
import SqlInjectionCustomizations::SqlInjection
9+
10+
/**
11+
* A taint-tracking configuration for detecting SQL injection vulnerabilities.
12+
*/
13+
class Configuration extends TaintTracking::Configuration {
14+
Configuration() { this = "SqlInjectionConfiguration" }
15+
16+
override predicate isSource(DataFlow::Node source) { source instanceof Source }
17+
18+
override predicate isSink(DataFlow::Node source) { source instanceof Sink }
19+
20+
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
21+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The `rb/sql-injection` query now considers consider SQL constructions, such as calls to `Arel.sql`, as sinks.

ruby/ql/src/queries/security/cwe-089/SqlInjection.ql

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,11 @@
1111
* external/cwe/cwe-089
1212
*/
1313

14-
import codeql.ruby.AST
15-
import codeql.ruby.Concepts
1614
import codeql.ruby.DataFlow
17-
import codeql.ruby.dataflow.BarrierGuards
18-
import codeql.ruby.dataflow.RemoteFlowSources
19-
import codeql.ruby.TaintTracking
15+
import codeql.ruby.security.SqlInjectionQuery
2016
import DataFlow::PathGraph
2117

22-
class SqlInjectionConfiguration extends TaintTracking::Configuration {
23-
SqlInjectionConfiguration() { this = "SQLInjectionConfiguration" }
24-
25-
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
26-
27-
override predicate isSink(DataFlow::Node sink) { sink instanceof SqlExecution }
28-
29-
override predicate isSanitizer(DataFlow::Node node) {
30-
node instanceof StringConstCompareBarrier or
31-
node instanceof StringConstArrayInclusionCallBarrier
32-
}
33-
}
34-
35-
from SqlInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
18+
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
3619
where config.hasFlowPath(source, sink)
3720
select sink.getNode(), source, sink, "This SQL query depends on a $@.", source.getNode(),
3821
"user-provided value"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| arel.rb:3:8:3:18 | call to sql | arel.rb:3:17:3:17 | x |
2+
| arel.rb:8:8:8:18 | call to sql | arel.rb:8:17:8:17 | x |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import codeql.ruby.Concepts
2+
import codeql.ruby.DataFlow
3+
4+
query predicate sqlConstructions(SqlConstruction c, DataFlow::Node sql) { sql = c.getSql() }

0 commit comments

Comments
 (0)