Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 35 additions & 14 deletions rust/ql/lib/codeql/rust/frameworks/stdlib/Builtins.qll
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,27 @@ class BuiltinType extends Struct {
string getName() { result = super.getName().getText() }
}

/**
* A numerical type, such as `i64`, `usize`, `f32` or `f64`.
*/
abstract private class NumericTypeImpl extends BuiltinType { }

final class NumericType = NumericTypeImpl;

/**
* An integral numerical type, such as `i64` or `usize`.
*/
abstract private class IntegralTypeImpl extends NumericTypeImpl { }

final class IntegralType = IntegralTypeImpl;

/**
* A floating-point numerical type, such as `f32` or `f64`.
*/
abstract private class FloatingPointTypeImpl extends NumericTypeImpl { }

final class FloatingPointType = FloatingPointTypeImpl;

/** The builtin `bool` type. */
class Bool extends BuiltinType {
Bool() { this.getName() = "bool" }
Expand All @@ -47,71 +68,71 @@ class Str extends BuiltinType {
}

/** The builtin `i8` type. */
class I8 extends BuiltinType {
class I8 extends IntegralTypeImpl {
I8() { this.getName() = "i8" }
}

/** The builtin `i16` type. */
class I16 extends BuiltinType {
class I16 extends IntegralTypeImpl {
I16() { this.getName() = "i16" }
}

/** The builtin `i32` type. */
class I32 extends BuiltinType {
class I32 extends IntegralTypeImpl {
I32() { this.getName() = "i32" }
}

/** The builtin `i64` type. */
class I64 extends BuiltinType {
class I64 extends IntegralTypeImpl {
I64() { this.getName() = "i64" }
}

/** The builtin `i128` type. */
class I128 extends BuiltinType {
class I128 extends IntegralTypeImpl {
I128() { this.getName() = "i128" }
}

/** The builtin `u8` type. */
class U8 extends BuiltinType {
class U8 extends IntegralTypeImpl {
U8() { this.getName() = "u8" }
}

/** The builtin `u16` type. */
class U16 extends BuiltinType {
class U16 extends IntegralTypeImpl {
U16() { this.getName() = "u16" }
}

/** The builtin `u32` type. */
class U32 extends BuiltinType {
class U32 extends IntegralTypeImpl {
U32() { this.getName() = "u32" }
}

/** The builtin `u64` type. */
class U64 extends BuiltinType {
class U64 extends IntegralTypeImpl {
U64() { this.getName() = "u64" }
}

/** The builtin `u128` type. */
class U128 extends BuiltinType {
class U128 extends IntegralTypeImpl {
U128() { this.getName() = "u128" }
}

/** The builtin `usize` type. */
class Usize extends BuiltinType {
class Usize extends IntegralTypeImpl {
Usize() { this.getName() = "usize" }
}

/** The builtin `isize` type. */
class Isize extends BuiltinType {
class Isize extends IntegralTypeImpl {
Isize() { this.getName() = "isize" }
}

/** The builtin `f32` type. */
class F32 extends BuiltinType {
class F32 extends FloatingPointTypeImpl {
F32() { this.getName() = "f32" }
}

/** The builtin `f64` type. */
class F64 extends BuiltinType {
class F64 extends FloatingPointTypeImpl {
F64() { this.getName() = "f64" }
}
42 changes: 42 additions & 0 deletions rust/ql/lib/codeql/rust/security/Barriers.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Classes to represent barriers commonly used in dataflow and taint tracking
* configurations.
*/

import rust
private import codeql.rust.dataflow.DataFlow
private import codeql.rust.internal.TypeInference as TypeInference
private import codeql.rust.internal.Type
private import codeql.rust.frameworks.stdlib.Builtins

/**
* A node whose type is a numeric or boolean type, which may be an appropriate
* taint flow barrier for some queries.
*/
class NumericTypeBarrier extends DataFlow::Node {
NumericTypeBarrier() {
exists(StructType t, Struct s |
t = TypeInference::inferType(this.asExpr().getExpr()) and
s = t.getStruct()
|
s instanceof NumericType or
s instanceof Bool
)
}
}

/**
* A node whose type is an integral (integer) or boolean type, which may be an
* appropriate taint flow barrier for some queries.
*/
class IntegralOrBooleanTypeBarrier extends DataFlow::Node {
IntegralOrBooleanTypeBarrier() {
exists(StructType t, Struct s |
t = TypeInference::inferType(this.asExpr().getExpr()) and
s = t.getStruct()
|
s instanceof IntegralType or
s instanceof Bool
)
}
}
7 changes: 7 additions & 0 deletions rust/ql/lib/codeql/rust/security/LogInjectionExtensions.qll
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ private import codeql.rust.dataflow.DataFlow
private import codeql.rust.dataflow.FlowSink
private import codeql.rust.Concepts
private import codeql.util.Unit
private import codeql.rust.security.Barriers as Barriers

/**
* Provides default sources, sinks and barriers for detecting log injection
Expand Down Expand Up @@ -42,4 +43,10 @@ module LogInjection {
private class ModelsAsDataSink extends Sink {
ModelsAsDataSink() { sinkNode(this, "log-injection") }
}

/**
* A barrier for log injection vulnerabilities for nodes whose type is a
* numeric or boolean type, which is unlikely to expose any vulnerability.
*/
private class NumericTypeBarrier extends Barrier instanceof Barriers::NumericTypeBarrier { }
}
7 changes: 7 additions & 0 deletions rust/ql/lib/codeql/rust/security/SqlInjectionExtensions.qll
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ private import codeql.rust.dataflow.DataFlow
private import codeql.rust.dataflow.FlowSink
private import codeql.rust.Concepts
private import codeql.util.Unit
private import codeql.rust.security.Barriers as Barriers

/**
* Provides default sources, sinks and barriers for detecting SQL injection
Expand Down Expand Up @@ -57,4 +58,10 @@ module SqlInjection {
private class ModelsAsDataSink extends Sink {
ModelsAsDataSink() { sinkNode(this, "sql-injection") }
}

/**
* A barrier for SQL injection vulnerabilities for nodes whose type is a numeric or
* boolean type, which is unlikely to expose any vulnerability.
*/
private class NumericTypeBarrier extends Barrier instanceof Barriers::NumericTypeBarrier { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ private import codeql.rust.dataflow.DataFlow
private import codeql.rust.controlflow.CfgNodes
private import codeql.rust.dataflow.FlowSink
private import codeql.rust.Concepts
private import codeql.rust.security.Barriers as Barriers

/**
* Provides default sources, sinks and barriers for detecting regular expression
Expand Down Expand Up @@ -87,4 +88,14 @@ module RegexInjection {
.getText() = "escape"
}
}

/**
* A barrier for regular expression injection vulnerabilities for nodes whose
* type is an integral or boolean type, which is unlikely to expose any vulnerability.
*
* We don't include floating point types in this barrier, as `.` is a special character
* in regular expressions.
*/
private class IntegralOrBooleanTypeBarrier extends Barrier instanceof Barriers::IntegralOrBooleanTypeBarrier
{ }
}
4 changes: 4 additions & 0 deletions rust/ql/src/change-notes/2025-10-31-barriers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Taint flow barriers have been added to the `rust/regex-injection`, `rust/sql-injection` and `rust/log-injection`, reducing the frequency of false positive results for these queries.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
| struct bool | |
| struct char | |
| struct f32 | FloatingPointType, NumericType |
| struct f64 | FloatingPointType, NumericType |
| struct i8 | IntegralType, NumericType |
| struct i16 | IntegralType, NumericType |
| struct i32 | IntegralType, NumericType |
| struct i64 | IntegralType, NumericType |
| struct i128 | IntegralType, NumericType |
| struct isize | IntegralType, NumericType |
| struct str | |
| struct u8 | IntegralType, NumericType |
| struct u16 | IntegralType, NumericType |
| struct u32 | IntegralType, NumericType |
| struct u64 | IntegralType, NumericType |
| struct u128 | IntegralType, NumericType |
| struct usize | IntegralType, NumericType |
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import rust
import codeql.rust.frameworks.stdlib.Builtins
import codeql.rust.internal.Type

string describe(BuiltinType t) {
t instanceof NumericType and result = "NumericType"
or
t instanceof IntegralType and result = "IntegralType"
or
t instanceof FloatingPointType and result = "FloatingPointType"
}

from BuiltinType t
select t.toString(), concat(describe(t), ", ")
7 changes: 7 additions & 0 deletions rust/ql/test/library-tests/elements/builtintypes/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions rust/ql/test/library-tests/elements/builtintypes/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

// --- tests ---

fn test_types() {
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
| main.rs:6:25:6:30 | &regex |
| main.rs:14:25:14:30 | &regex |
| main.rs:21:25:21:30 | &regex |
12 changes: 10 additions & 2 deletions rust/ql/test/query-tests/security/CWE-020/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@ fn simple_bad(hay: &str) -> Option<bool> {
Some(re.is_match(hay))
}

fn simple_good(hay: &str) -> Option<bool> {
fn simple_good_escaped(hay: &str) -> Option<bool> {
let username = std::env::var("USER").unwrap_or("".to_string());
let escaped = regex::escape(&username);
let regex = format!("foo{}bar", escaped);
let re = Regex::new(&regex).unwrap();
Some(re.is_match(hay))
}

fn simple_good_numeric(hay: &str) -> Option<bool> {
let user_number = std::env::var("USER").unwrap_or("0".to_string()).parse::<u64>().unwrap_or(0);
let regex = format!("foo{}bar", user_number);
let re = Regex::new(&regex).unwrap();
Some(re.is_match(hay))
}

fn not_a_sink_literal() -> Option<bool> {
let username = std::env::var("USER").unwrap_or("".to_string());
let re = Regex::new("literal string").unwrap();
Expand All @@ -30,7 +37,8 @@ fn not_a_sink_raw_literal() -> Option<bool> {
fn main() {
let hay = "a string";
simple_bad(hay);
simple_good(hay);
simple_good_escaped(hay);
simple_good_numeric(hay);
not_a_sink_literal();
not_a_sink_raw_literal();
}
Loading