Skip to content

Commit dd8d04b

Browse files
authored
Merge branch 'main' into post-release-prep/codeql-cli-2.22.2
2 parents 37cc782 + 6efc19d commit dd8d04b

26 files changed

+3435
-2707
lines changed

rust/ql/integration-tests/query-suite/rust-code-scanning.qls.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ ql/rust/ql/src/queries/security/CWE-312/CleartextLogging.ql
1515
ql/rust/ql/src/queries/security/CWE-327/BrokenCryptoAlgorithm.ql
1616
ql/rust/ql/src/queries/security/CWE-328/WeakSensitiveDataHashing.ql
1717
ql/rust/ql/src/queries/security/CWE-770/UncontrolledAllocationSize.ql
18+
ql/rust/ql/src/queries/security/CWE-798/HardcodedCryptographicValue.ql
1819
ql/rust/ql/src/queries/security/CWE-825/AccessInvalidPointer.ql
1920
ql/rust/ql/src/queries/summary/LinesOfCode.ql
2021
ql/rust/ql/src/queries/summary/LinesOfUserCode.ql

rust/ql/integration-tests/query-suite/rust-security-and-quality.qls.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ ql/rust/ql/src/queries/security/CWE-327/BrokenCryptoAlgorithm.ql
1616
ql/rust/ql/src/queries/security/CWE-328/WeakSensitiveDataHashing.ql
1717
ql/rust/ql/src/queries/security/CWE-696/BadCtorInitialization.ql
1818
ql/rust/ql/src/queries/security/CWE-770/UncontrolledAllocationSize.ql
19+
ql/rust/ql/src/queries/security/CWE-798/HardcodedCryptographicValue.ql
1920
ql/rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.ql
2021
ql/rust/ql/src/queries/security/CWE-825/AccessInvalidPointer.ql
2122
ql/rust/ql/src/queries/summary/LinesOfCode.ql

rust/ql/integration-tests/query-suite/rust-security-extended.qls.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ ql/rust/ql/src/queries/security/CWE-312/CleartextLogging.ql
1515
ql/rust/ql/src/queries/security/CWE-327/BrokenCryptoAlgorithm.ql
1616
ql/rust/ql/src/queries/security/CWE-328/WeakSensitiveDataHashing.ql
1717
ql/rust/ql/src/queries/security/CWE-770/UncontrolledAllocationSize.ql
18+
ql/rust/ql/src/queries/security/CWE-798/HardcodedCryptographicValue.ql
1819
ql/rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.ql
1920
ql/rust/ql/src/queries/security/CWE-825/AccessInvalidPointer.ql
2021
ql/rust/ql/src/queries/summary/LinesOfCode.ql
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
extensions:
2+
- addsTo:
3+
pack: codeql/rust-all
4+
extensible: summaryModel
5+
data:
6+
- ["<generic_array::GenericArray>::from_slice", "Argument[0].Reference", "ReturnValue.Reference", "value", "manual"]
7+
- ["<generic_array::GenericArray>::from_mut_slice", "Argument[0].Reference", "ReturnValue.Reference", "value", "manual"]
8+
- ["<generic_array::GenericArray>::try_from_slice", "Argument[0].Reference", "ReturnValue.Field[crate::result::Result::Ok(0)].Reference", "value", "manual"]
9+
- ["<generic_array::GenericArray>::try_from_mut_slice", "Argument[0].Reference", "ReturnValue.Field[crate::result::Result::Ok(0)].Reference", "value", "manual"]

rust/ql/lib/codeql/rust/frameworks/rustcrypto/rustcrypto.model.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,28 @@ extensions:
88
- ["<_ as digest::digest::Digest>::chain_update", "Argument[0]", "hasher-input", "manual"]
99
- ["<_ as digest::digest::Digest>::digest", "Argument[0]", "hasher-input", "manual"]
1010
- ["md5::compute", "Argument[0]", "hasher-input", "manual"]
11+
- ["<_ as crypto_common::KeyInit>::new", "Argument[0]", "credentials-key", "manual"]
12+
- ["<_ as crypto_common::KeyInit>::new", "Argument[1]", "credentials-iv", "manual"]
13+
- ["<_ as crypto_common::KeyInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"]
14+
- ["<_ as crypto_common::KeyInit>::new_from_slice", "Argument[1]", "credentials-iv", "manual"]
15+
- ["<_ as crypto_common::KeyIvInit>::new", "Argument[0]", "credentials-key", "manual"]
16+
- ["<_ as crypto_common::KeyIvInit>::new", "Argument[1]", "credentials-iv", "manual"]
17+
- ["<_ as crypto_common::KeyIvInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"]
18+
- ["<_ as crypto_common::KeyIvInit>::new_from_slice", "Argument[1]", "credentials-iv", "manual"]
19+
- ["<cipher::stream_wrapper::StreamCipherCoreWrapper as crypto_common::KeyInit>::new", "Argument[0]", "credentials-key", "manual"]
20+
- ["<cipher::stream_wrapper::StreamCipherCoreWrapper as crypto_common::KeyInit>::new", "Argument[1]", "credentials-iv", "manual"]
21+
- ["<cipher::stream_wrapper::StreamCipherCoreWrapper as crypto_common::KeyInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"]
22+
- ["<cipher::stream_wrapper::StreamCipherCoreWrapper as crypto_common::KeyInit>::new_from_slice", "Argument[1]", "credentials-iv", "manual"]
23+
- ["<cipher::stream_wrapper::StreamCipherCoreWrapper as crypto_common::KeyIvInit>::new", "Argument[0]", "credentials-key", "manual"]
24+
- ["<cipher::stream_wrapper::StreamCipherCoreWrapper as crypto_common::KeyIvInit>::new", "Argument[1]", "credentials-iv", "manual"]
25+
- ["<cipher::stream_wrapper::StreamCipherCoreWrapper as crypto_common::KeyIvInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"]
26+
- ["<cipher::stream_wrapper::StreamCipherCoreWrapper as crypto_common::KeyIvInit>::new_from_slice", "Argument[1]", "credentials-iv", "manual"]
27+
- ["<digest::core_api::wrapper::CoreWrapper as crypto_common::KeyInit>::new", "Argument[0]", "credentials-key", "manual"]
28+
- ["<digest::core_api::wrapper::CoreWrapper as crypto_common::KeyInit>::new", "Argument[1]", "credentials-iv", "manual"]
29+
- ["<digest::core_api::wrapper::CoreWrapper as crypto_common::KeyInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"]
30+
- ["<digest::core_api::wrapper::CoreWrapper as crypto_common::KeyInit>::new_from_slice", "Argument[1]", "credentials-iv", "manual"]
31+
- ["<digest::core_api::wrapper::CoreWrapper as crypto_common::KeyIvInit>::new", "Argument[0]", "credentials-key", "manual"]
32+
- ["<digest::core_api::wrapper::CoreWrapper as crypto_common::KeyIvInit>::new", "Argument[1]", "credentials-iv", "manual"]
33+
- ["<digest::core_api::wrapper::CoreWrapper as crypto_common::KeyIvInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"]
34+
- ["<digest::core_api::wrapper::CoreWrapper as crypto_common::KeyIvInit>::new_from_slice", "Argument[1]", "credentials-iv", "manual"]
35+
- ["<_ as aead::Aead>::encrypt", "Argument[0]", "credentials-nonce", "manual"]

rust/ql/lib/codeql/rust/frameworks/stdlib/lang-core.model.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ extensions:
33
pack: codeql/rust-all
44
extensible: summaryModel
55
data:
6+
# Conversions
7+
- ["<core::alloc::layout::Layout>::align_to", "Argument[self].Element", "ReturnValue.Field[0,1,2].Reference.Element", "taint", "manual"]
8+
- ["<_ as core::convert::Into>::into", "Argument[self].Element", "ReturnValue.Element", "taint", "manual"]
9+
- ["<_ as core::convert::Into>::into", "Argument[self].Reference.Element", "ReturnValue.Element", "taint", "manual"]
10+
- ["<alloc::string::String as core::convert::Into>::into", "Argument[self].Element", "ReturnValue.Element", "taint", "manual"]
11+
- ["<alloc::string::String as core::convert::Into>::into", "Argument[self].Reference.Element", "ReturnValue.Element", "taint", "manual"]
612
# Iterator
713
- ["<core::result::Result>::iter", "Argument[self].Element", "ReturnValue.Element", "value", "manual"]
814
- ["<alloc::vec::Vec as value_trait::array::Array>::iter", "Argument[self].Element", "ReturnValue.Element", "value", "manual"]
@@ -59,6 +65,8 @@ extensions:
5965
pack: codeql/rust-all
6066
extensible: sourceModel
6167
data:
68+
# Mem
69+
- ["core::mem::zeroed", "ReturnValue.Element", "constant-source", "manual"]
6270
# Ptr
6371
- ["core::ptr::drop_in_place", "Argument[0]", "pointer-invalidate", "manual"]
6472
- ["core::ptr::dangling", "ReturnValue", "pointer-invalidate", "manual"]

rust/ql/lib/codeql/rust/internal/PathResolution.qll

Lines changed: 66 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,18 @@ abstract class ItemNode extends Locatable {
112112
result = this.(SourceFileItemNode).getSuper()
113113
}
114114

115+
pragma[nomagic]
116+
private ItemNode getAChildSuccessor(string name) {
117+
this = result.getImmediateParent() and
118+
name = result.getName()
119+
}
120+
115121
cached
116122
ItemNode getASuccessorRec(string name) {
117123
Stages::PathResolutionStage::ref() and
118124
sourceFileEdge(this, name, result)
119125
or
120-
this = result.getImmediateParent() and
121-
name = result.getName()
126+
result = this.getAChildSuccessor(name)
122127
or
123128
fileImportEdge(this, name, result)
124129
or
@@ -224,6 +229,38 @@ abstract class ItemNode extends Locatable {
224229
result.(CrateItemNode).isPotentialDollarCrateTarget()
225230
}
226231

232+
/**
233+
* Holds if the successor `item` with the name `name` is not available locally
234+
* for unqualified paths.
235+
*
236+
* This has the effect that a path of the form `name` inside `this` will not
237+
* resolve to `item`.
238+
*/
239+
pragma[nomagic]
240+
predicate excludedLocally(string name, ItemNode item) {
241+
// Associated items in an impl or trait block are not directly available
242+
// inside the block, they require a qualified path with a `Self` prefix.
243+
item = this.getAChildSuccessor(name) and
244+
this instanceof ImplOrTraitItemNode and
245+
item instanceof AssocItemNode
246+
}
247+
248+
/**
249+
* Holds if the successor `item` with the name `name` is not available
250+
* externally for qualified paths that resolve to this item.
251+
*
252+
* This has the effect that a path of the form `Qualifier::name`, where
253+
* `Qualifier` resolves to this item, will not resolve to `item`.
254+
*/
255+
pragma[nomagic]
256+
predicate excludedExternally(string name, ItemNode item) {
257+
// Type parameters for an `impl` or trait block are not available outside of
258+
// the block.
259+
item = this.getAChildSuccessor(name) and
260+
this instanceof ImplOrTraitItemNode and
261+
item instanceof TypeParamItemNode
262+
}
263+
227264
pragma[nomagic]
228265
private predicate hasSourceFunction(string name) {
229266
this.getASuccessorFull(name).(Function).fromSource()
@@ -1145,7 +1182,9 @@ pragma[nomagic]
11451182
private predicate declares(ItemNode item, Namespace ns, string name) {
11461183
exists(ItemNode child | child.getImmediateParent() = item |
11471184
child.getName() = name and
1148-
child.getNamespace() = ns
1185+
child.getNamespace() = ns and
1186+
// If `item` is excluded locally then it does not declare `name`.
1187+
not item.excludedLocally(name, child)
11491188
or
11501189
useTreeDeclares(child.(Use).getUseTree(), name) and
11511190
exists(ns) // `use foo::bar` can refer to both a value and a type
@@ -1193,38 +1232,27 @@ private ItemNode getOuterScope(ItemNode i) {
11931232
result = i.getImmediateParent()
11941233
}
11951234

1196-
pragma[nomagic]
1197-
private ItemNode getAdjustedEnclosing(ItemNode encl0, Namespace ns) {
1198-
// functions in `impl` blocks need to use explicit `Self::` to access other
1199-
// functions in the `impl` block
1200-
if encl0 instanceof ImplOrTraitItemNode and ns.isValue()
1201-
then result = encl0.getImmediateParent()
1202-
else result = encl0
1203-
}
1204-
12051235
/**
12061236
* Holds if the unqualified path `p` references an item named `name`, and `name`
12071237
* may be looked up in the `ns` namespace inside enclosing item `encl`.
12081238
*/
12091239
pragma[nomagic]
12101240
private predicate unqualifiedPathLookup(ItemNode encl, string name, Namespace ns, RelevantPath p) {
1211-
exists(ItemNode encl0 | encl = getAdjustedEnclosing(encl0, ns) |
1212-
// lookup in the immediately enclosing item
1213-
p.isUnqualified(name) and
1214-
encl0.getADescendant() = p and
1215-
exists(ns) and
1216-
not name = ["crate", "$crate", "super", "self"]
1217-
or
1218-
// lookup in an outer scope, but only if the item is not declared in inner scope
1219-
exists(ItemNode mid |
1220-
unqualifiedPathLookup(mid, name, ns, p) and
1221-
not declares(mid, ns, name) and
1222-
not (
1223-
name = "Self" and
1224-
mid = any(ImplOrTraitItemNode i).getAnItemInSelfScope()
1225-
) and
1226-
encl0 = getOuterScope(mid)
1227-
)
1241+
// lookup in the immediately enclosing item
1242+
p.isUnqualified(name) and
1243+
encl.getADescendant() = p and
1244+
exists(ns) and
1245+
not name = ["crate", "$crate", "super", "self"]
1246+
or
1247+
// lookup in an outer scope, but only if the item is not declared in inner scope
1248+
exists(ItemNode mid |
1249+
unqualifiedPathLookup(mid, name, ns, p) and
1250+
not declares(mid, ns, name) and
1251+
not (
1252+
name = "Self" and
1253+
mid = any(ImplOrTraitItemNode i).getAnItemInSelfScope()
1254+
) and
1255+
encl = getOuterScope(mid)
12281256
)
12291257
}
12301258

@@ -1245,10 +1273,10 @@ private predicate sourceFileHasCratePathTc(ItemNode i1, ItemNode i2) =
12451273

12461274
/**
12471275
* Holds if the unqualified path `p` references a keyword item named `name`, and
1248-
* `name` may be looked up in the `ns` namespace inside enclosing item `encl`.
1276+
* `name` may be looked up inside enclosing item `encl`.
12491277
*/
12501278
pragma[nomagic]
1251-
private predicate keywordLookup(ItemNode encl, string name, Namespace ns, RelevantPath p) {
1279+
private predicate keywordLookup(ItemNode encl, string name, RelevantPath p) {
12521280
// For `($)crate`, jump directly to the root module
12531281
exists(ItemNode i | p.isCratePath(name, i) |
12541282
encl instanceof SourceFile and
@@ -1259,18 +1287,17 @@ private predicate keywordLookup(ItemNode encl, string name, Namespace ns, Releva
12591287
or
12601288
name = ["super", "self"] and
12611289
p.isUnqualified(name) and
1262-
exists(ItemNode encl0 |
1263-
encl0.getADescendant() = p and
1264-
encl = getAdjustedEnclosing(encl0, ns)
1265-
)
1290+
encl.getADescendant() = p
12661291
}
12671292

12681293
pragma[nomagic]
12691294
private ItemNode unqualifiedPathLookup(RelevantPath p, Namespace ns) {
1270-
exists(ItemNode encl, string name | result = getASuccessorFull(encl, name, ns) |
1295+
exists(ItemNode encl, string name |
1296+
result = getASuccessorFull(encl, name, ns) and not encl.excludedLocally(name, result)
1297+
|
12711298
unqualifiedPathLookup(encl, name, ns, p)
12721299
or
1273-
keywordLookup(encl, name, ns, p)
1300+
keywordLookup(encl, name, p) and exists(ns)
12741301
)
12751302
}
12761303

@@ -1291,7 +1318,8 @@ private ItemNode resolvePath0(RelevantPath path, Namespace ns) {
12911318
or
12921319
exists(ItemNode q, string name |
12931320
q = resolvePathQualifier(path, name) and
1294-
result = getASuccessorFull(q, name, ns)
1321+
result = getASuccessorFull(q, name, ns) and
1322+
not q.excludedExternally(name, result)
12951323
)
12961324
or
12971325
result = resolveUseTreeListItem(_, _, path) and

rust/ql/lib/codeql/rust/security/CleartextLoggingExtensions.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import rust
77
private import codeql.rust.dataflow.DataFlow
8-
private import codeql.rust.dataflow.internal.DataFlowImpl
8+
private import codeql.rust.dataflow.FlowSink
99
private import codeql.rust.security.SensitiveData
1010
private import codeql.rust.Concepts
1111

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/**
2+
* Provides classes and predicates for reasoning about hard-coded cryptographic value
3+
* vulnerabilities.
4+
*/
5+
6+
import rust
7+
private import codeql.rust.dataflow.DataFlow
8+
private import codeql.rust.dataflow.FlowSource
9+
private import codeql.rust.dataflow.FlowSink
10+
private import codeql.rust.Concepts
11+
private import codeql.rust.security.SensitiveData
12+
13+
/**
14+
* A kind of cryptographic value.
15+
*/
16+
class CryptographicValueKind extends string {
17+
CryptographicValueKind() { this = ["password", "key", "iv", "nonce", "salt"] }
18+
19+
/**
20+
* Gets a description of this value kind for user-facing messages.
21+
*/
22+
string getDescription() {
23+
this = "password" and result = "a password"
24+
or
25+
this = "key" and result = "a key"
26+
or
27+
this = "iv" and result = "an initialization vector"
28+
or
29+
this = "nonce" and result = "a nonce"
30+
or
31+
this = "salt" and result = "a salt"
32+
}
33+
}
34+
35+
/**
36+
* Provides default sources, sinks and barriers for detecting hard-coded cryptographic
37+
* value vulnerabilities, as well as extension points for adding your own.
38+
*/
39+
module HardcodedCryptographicValue {
40+
/**
41+
* A data flow source for hard-coded cryptographic value vulnerabilities.
42+
*/
43+
abstract class Source extends DataFlow::Node { }
44+
45+
/**
46+
* A data flow sink for hard-coded cryptographic value vulnerabilities.
47+
*/
48+
abstract class Sink extends QuerySink::Range {
49+
override string getSinkType() { result = "HardcodedCryptographicValue" }
50+
51+
/**
52+
* Gets the kind of credential this sink is interpreted as.
53+
*/
54+
abstract CryptographicValueKind getKind();
55+
}
56+
57+
/**
58+
* A barrier for hard-coded cryptographic value vulnerabilities.
59+
*/
60+
abstract class Barrier extends DataFlow::Node { }
61+
62+
/**
63+
* A literal, considered as a flow source.
64+
*/
65+
private class LiteralSource extends Source {
66+
LiteralSource() { this.asExpr().getExpr() instanceof LiteralExpr }
67+
}
68+
69+
/**
70+
* An array initialized from a list of literals, considered as a single flow source. For example:
71+
* ```
72+
* `[0, 0, 0, 0]`
73+
* ```
74+
*/
75+
private class ArrayListSource extends Source {
76+
ArrayListSource() { this.asExpr().getExpr().(ArrayListExpr).getExpr(_) instanceof LiteralExpr }
77+
}
78+
79+
/**
80+
* An externally modeled source for constant values.
81+
*/
82+
private class ModeledSource extends Source {
83+
ModeledSource() { sourceNode(this, "constant-source") }
84+
}
85+
86+
/**
87+
* An externally modeled sink for hard-coded cryptographic value vulnerabilities.
88+
*/
89+
private class ModelsAsDataSinks extends Sink {
90+
CryptographicValueKind kind;
91+
92+
ModelsAsDataSinks() { sinkNode(this, "credentials-" + kind) }
93+
94+
override CryptographicValueKind getKind() { result = kind }
95+
}
96+
97+
/**
98+
* A call to `getrandom` that is a barrier.
99+
*/
100+
private class GetRandomBarrier extends Barrier {
101+
GetRandomBarrier() {
102+
exists(CallExprBase ce |
103+
ce.getStaticTarget().(Addressable).getCanonicalPath() =
104+
["getrandom::fill", "getrandom::getrandom"] and
105+
this.asExpr().getExpr().getParentNode*() = ce.getArgList().getArg(0)
106+
)
107+
}
108+
}
109+
}

rust/ql/lib/codeql/rust/security/SqlInjectionExtensions.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import rust
88
private import codeql.rust.dataflow.DataFlow
9-
private import codeql.rust.dataflow.internal.DataFlowImpl
9+
private import codeql.rust.dataflow.FlowSink
1010
private import codeql.rust.Concepts
1111
private import codeql.util.Unit
1212

0 commit comments

Comments
 (0)