mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Ruby: Desugar hash literals
```rb
{ a: 1, **splat, b: 2 }
```
becomes
```rb
::Hash.[](a: 1, **splat, b: 2)
```
This commit is contained in:
@@ -794,14 +794,15 @@ private module ArrayLiteralDesugar {
|
||||
exists(ArrayLiteral al |
|
||||
parent = al and
|
||||
i = -1 and
|
||||
child = SynthChild(MethodCallKind("[]", false, al.getNumberOfElements() + 1))
|
||||
child = SynthChild(MethodCallKind("[]", false, al.getNumberOfElements()))
|
||||
or
|
||||
exists(AstNode mc | mc = TMethodCallSynth(al, -1, _, _, _) |
|
||||
parent = mc and
|
||||
exists(AstNode mc |
|
||||
mc = TMethodCallSynth(al, -1, _, _, _) and
|
||||
parent = mc
|
||||
|
|
||||
i = 0 and
|
||||
child = SynthChild(ConstantReadAccessKind("::Array"))
|
||||
or
|
||||
parent = mc and
|
||||
child = childRef(al.getElement(i - 1))
|
||||
)
|
||||
)
|
||||
@@ -825,13 +826,58 @@ private module ArrayLiteralDesugar {
|
||||
final override predicate methodCall(string name, boolean setter, int arity) {
|
||||
name = "[]" and
|
||||
setter = false and
|
||||
arity = any(ArrayLiteral al).getNumberOfElements() + 1
|
||||
arity = any(ArrayLiteral al).getNumberOfElements()
|
||||
}
|
||||
|
||||
final override predicate constantReadAccess(string name) { name = "::Array" }
|
||||
}
|
||||
}
|
||||
|
||||
private module HashLiteralDesugar {
|
||||
pragma[nomagic]
|
||||
private predicate hashLiteralSynthesis(AstNode parent, int i, Child child) {
|
||||
exists(HashLiteral hl |
|
||||
parent = hl and
|
||||
i = -1 and
|
||||
child = SynthChild(MethodCallKind("[]", false, hl.getNumberOfElements()))
|
||||
or
|
||||
exists(AstNode mc |
|
||||
mc = TMethodCallSynth(hl, -1, _, _, _) and
|
||||
parent = mc
|
||||
|
|
||||
i = 0 and
|
||||
child = SynthChild(ConstantReadAccessKind("::Hash"))
|
||||
or
|
||||
child = childRef(hl.getElement(i - 1))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* ```rb
|
||||
* { a: 1, **splat, b: 2 }
|
||||
* ```
|
||||
* desugars to
|
||||
*
|
||||
* ```rb
|
||||
* ::Hash.[](a: 1, **splat, b: 2)
|
||||
* ```
|
||||
*/
|
||||
private class HashLiteralSynthesis extends Synthesis {
|
||||
final override predicate child(AstNode parent, int i, Child child) {
|
||||
hashLiteralSynthesis(parent, i, child)
|
||||
}
|
||||
|
||||
final override predicate methodCall(string name, boolean setter, int arity) {
|
||||
name = "[]" and
|
||||
setter = false and
|
||||
arity = any(HashLiteral hl).getNumberOfElements()
|
||||
}
|
||||
|
||||
final override predicate constantReadAccess(string name) { name = "::Hash" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ```rb
|
||||
* for x in xs
|
||||
|
||||
@@ -484,6 +484,9 @@ module ExprNodes {
|
||||
/** Gets the `n`th argument of this call. */
|
||||
final ExprCfgNode getArgument(int n) { e.hasCfgChild(e.getArgument(n), this, result) }
|
||||
|
||||
/** Gets an argument of this call. */
|
||||
final ExprCfgNode getAnArgument() { result = this.getArgument(_) }
|
||||
|
||||
/** Gets the the keyword argument whose key is `keyword` of this call. */
|
||||
final ExprCfgNode getKeywordArgument(string keyword) {
|
||||
exists(PairCfgNode n |
|
||||
@@ -940,4 +943,39 @@ module ExprNodes {
|
||||
|
||||
final override ElementReference getExpr() { result = super.getExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A control-flow node that wraps an array literal. Array literals are desugared
|
||||
* into calls to `Array.[]`, so this includes both desugared calls as well as
|
||||
* explicit calls.
|
||||
*/
|
||||
class ArrayLiteralCfgNode extends MethodCallCfgNode {
|
||||
ArrayLiteralCfgNode() {
|
||||
exists(ConstantReadAccess array |
|
||||
array = this.getReceiver().getExpr() and
|
||||
e.(MethodCall).getMethodName() = "[]" and
|
||||
array.getName() = "Array" and
|
||||
array.hasGlobalScope()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A control-flow node that wraps a hash literal. Hash literals are desugared
|
||||
* into calls to `Hash.[]`, so this includes both desugared calls as well as
|
||||
* explicit calls.
|
||||
*/
|
||||
class HashLiteralCfgNode extends MethodCallCfgNode {
|
||||
HashLiteralCfgNode() {
|
||||
exists(ConstantReadAccess array |
|
||||
array = this.getReceiver().getExpr() and
|
||||
e.(MethodCall).getMethodName() = "[]" and
|
||||
array.getName() = "Hash" and
|
||||
array.hasGlobalScope()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a pair of this hash literal. */
|
||||
PairCfgNode getAKeyValuePair() { result = this.getAnArgument() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -986,10 +986,6 @@ module Trees {
|
||||
|
||||
private class GlobalVariableTree extends LeafTree, GlobalVariableAccess { }
|
||||
|
||||
private class HashLiteralTree extends StandardPostOrderTree, HashLiteral {
|
||||
final override ControlFlowTree getChildElement(int i) { result = this.getElement(i) }
|
||||
}
|
||||
|
||||
private class HashSplatNilParameterTree extends LeafTree, HashSplatNilParameter { }
|
||||
|
||||
private class HashSplatParameterTree extends NonDefaultValueParameterTree, HashSplatParameter { }
|
||||
|
||||
@@ -278,16 +278,9 @@ private DataFlow::LocalSourceNode trackInstance(Module tp, TypeTracker t) {
|
||||
or
|
||||
result.asExpr().getExpr() instanceof StringlikeLiteral and tp = TResolved("String")
|
||||
or
|
||||
exists(ConstantReadAccess array, MethodCall mc |
|
||||
result.asExpr().getExpr() = mc and
|
||||
mc.getMethodName() = "[]" and
|
||||
mc.getReceiver() = array and
|
||||
array.getName() = "Array" and
|
||||
array.hasGlobalScope() and
|
||||
tp = TResolved("Array")
|
||||
)
|
||||
result.asExpr() instanceof CfgNodes::ExprNodes::ArrayLiteralCfgNode and tp = TResolved("Array")
|
||||
or
|
||||
result.asExpr().getExpr() instanceof HashLiteral and tp = TResolved("Hash")
|
||||
result.asExpr() instanceof CfgNodes::ExprNodes::HashLiteralCfgNode and tp = TResolved("Hash")
|
||||
or
|
||||
result.asExpr().getExpr() instanceof MethodBase and tp = TResolved("Symbol")
|
||||
or
|
||||
|
||||
@@ -52,14 +52,15 @@ private class LibXmlRubyXmlParserCall extends XmlParserCall::Range, DataFlow::Ca
|
||||
override DataFlow::Node getInput() { result = this.getArgument(0) }
|
||||
|
||||
override predicate externalEntitiesEnabled() {
|
||||
exists(Pair pair |
|
||||
pair = this.getArgument(1).asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
exists(CfgNodes::ExprNodes::PairCfgNode pair |
|
||||
pair =
|
||||
this.getArgument(1).asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
|
||||
pair.getKey().getConstantValue().isStringOrSymbol("options") and
|
||||
pair.getValue() =
|
||||
[
|
||||
trackEnableFeature(TNOENT()), trackEnableFeature(TDTDLOAD()),
|
||||
trackDisableFeature(TNONET())
|
||||
].asExpr().getExpr()
|
||||
].asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import ruby
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
|
||||
@@ -91,16 +92,16 @@ class ExconHttpRequest extends HTTP::Client::Request::Range {
|
||||
predicate argSetsVerifyPeer(DataFlow::Node arg, boolean value, DataFlow::Node kvNode) {
|
||||
// Either passed as an individual key:value argument, e.g.:
|
||||
// Excon.get(..., ssl_verify_peer: false)
|
||||
isSslVerifyPeerPair(arg.asExpr().getExpr(), value) and
|
||||
isSslVerifyPeerPair(arg.asExpr(), value) and
|
||||
kvNode = arg
|
||||
or
|
||||
// Or as a single hash argument, e.g.:
|
||||
// Excon.get(..., { ssl_verify_peer: false, ... })
|
||||
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
|
||||
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
|
||||
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
|
||||
isSslVerifyPeerPair(p, value) and
|
||||
optionsNode.flowsTo(arg) and
|
||||
kvNode.asExpr().getExpr() = p
|
||||
kvNode.asExpr() = p
|
||||
)
|
||||
}
|
||||
|
||||
@@ -133,10 +134,10 @@ private predicate hasBooleanValue(DataFlow::Node node, boolean value) {
|
||||
}
|
||||
|
||||
/** Holds if `p` is the pair `ssl_verify_peer: <value>`. */
|
||||
private predicate isSslVerifyPeerPair(Pair p, boolean value) {
|
||||
private predicate isSslVerifyPeerPair(CfgNodes::ExprNodes::PairCfgNode p, boolean value) {
|
||||
exists(DataFlow::Node key, DataFlow::Node valueNode |
|
||||
key.asExpr().getExpr() = p.getKey() and valueNode.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
valueNode.asExpr() = p.getValue() and
|
||||
isSslVerifyPeerLiteral(key) and
|
||||
hasBooleanValue(valueNode, value)
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import ruby
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
|
||||
@@ -56,16 +57,16 @@ class FaradayHttpRequest extends HTTP::Client::Request::Range {
|
||||
|
|
||||
// Either passed as an individual key:value argument, e.g.:
|
||||
// Faraday.new(..., ssl: {...})
|
||||
isSslOptionsPairDisablingValidation(arg.asExpr().getExpr()) and
|
||||
isSslOptionsPairDisablingValidation(arg.asExpr()) and
|
||||
disablingNode = arg
|
||||
or
|
||||
// Or as a single hash argument, e.g.:
|
||||
// Faraday.new(..., { ssl: {...} })
|
||||
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
|
||||
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
|
||||
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
|
||||
isSslOptionsPairDisablingValidation(p) and
|
||||
optionsNode.flowsTo(arg) and
|
||||
disablingNode.asExpr().getExpr() = p
|
||||
disablingNode.asExpr() = p
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -78,10 +79,10 @@ class FaradayHttpRequest extends HTTP::Client::Request::Range {
|
||||
* containing either `verify: false` or
|
||||
* `verify_mode: OpenSSL::SSL::VERIFY_NONE`.
|
||||
*/
|
||||
private predicate isSslOptionsPairDisablingValidation(Pair p) {
|
||||
private predicate isSslOptionsPairDisablingValidation(CfgNodes::ExprNodes::PairCfgNode p) {
|
||||
exists(DataFlow::Node key, DataFlow::Node value |
|
||||
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
value.asExpr() = p.getValue() and
|
||||
isSymbolLiteral(key, "ssl") and
|
||||
(isHashWithVerifyFalse(value) or isHashWithVerifyModeNone(value))
|
||||
)
|
||||
@@ -101,7 +102,7 @@ private predicate isSymbolLiteral(DataFlow::Node node, string valueText) {
|
||||
*/
|
||||
private predicate isHashWithVerifyFalse(DataFlow::Node node) {
|
||||
exists(DataFlow::LocalSourceNode hash |
|
||||
isVerifyFalsePair(hash.asExpr().getExpr().(HashLiteral).getAKeyValuePair()) and
|
||||
isVerifyFalsePair(hash.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair()) and
|
||||
hash.flowsTo(node)
|
||||
)
|
||||
}
|
||||
@@ -112,7 +113,7 @@ private predicate isHashWithVerifyFalse(DataFlow::Node node) {
|
||||
*/
|
||||
private predicate isHashWithVerifyModeNone(DataFlow::Node node) {
|
||||
exists(DataFlow::LocalSourceNode hash |
|
||||
isVerifyModeNonePair(hash.asExpr().getExpr().(HashLiteral).getAKeyValuePair()) and
|
||||
isVerifyModeNonePair(hash.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair()) and
|
||||
hash.flowsTo(node)
|
||||
)
|
||||
}
|
||||
@@ -121,10 +122,10 @@ private predicate isHashWithVerifyModeNone(DataFlow::Node node) {
|
||||
* Holds if the pair `p` has the key `:verify_mode` and the value
|
||||
* `OpenSSL::SSL::VERIFY_NONE`.
|
||||
*/
|
||||
private predicate isVerifyModeNonePair(Pair p) {
|
||||
private predicate isVerifyModeNonePair(CfgNodes::ExprNodes::PairCfgNode p) {
|
||||
exists(DataFlow::Node key, DataFlow::Node value |
|
||||
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
value.asExpr() = p.getValue() and
|
||||
isSymbolLiteral(key, "verify_mode") and
|
||||
value = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").getAUse()
|
||||
)
|
||||
@@ -133,10 +134,10 @@ private predicate isVerifyModeNonePair(Pair p) {
|
||||
/**
|
||||
* Holds if the pair `p` has the key `:verify` and the value `false`.
|
||||
*/
|
||||
private predicate isVerifyFalsePair(Pair p) {
|
||||
private predicate isVerifyFalsePair(CfgNodes::ExprNodes::PairCfgNode p) {
|
||||
exists(DataFlow::Node key, DataFlow::Node value |
|
||||
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
value.asExpr() = p.getValue() and
|
||||
isSymbolLiteral(key, "verify") and
|
||||
isFalse(value)
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import ruby
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
|
||||
@@ -48,20 +49,21 @@ class HttpartyRequest extends HTTP::Client::Request::Range {
|
||||
// argument, and we're looking for `{ verify: false }` or
|
||||
// `{ verify_peer: false }`.
|
||||
exists(DataFlow::Node arg, int i |
|
||||
i > 0 and arg.asExpr().getExpr() = requestUse.asExpr().getExpr().(MethodCall).getArgument(i)
|
||||
i > 0 and
|
||||
arg.asExpr() = requestUse.asExpr().(CfgNodes::ExprNodes::MethodCallCfgNode).getArgument(i)
|
||||
|
|
||||
// Either passed as an individual key:value argument, e.g.:
|
||||
// HTTParty.get(..., verify: false)
|
||||
isVerifyFalsePair(arg.asExpr().getExpr()) and
|
||||
isVerifyFalsePair(arg.asExpr()) and
|
||||
disablingNode = arg
|
||||
or
|
||||
// Or as a single hash argument, e.g.:
|
||||
// HTTParty.get(..., { verify: false, ... })
|
||||
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
|
||||
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
|
||||
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
|
||||
isVerifyFalsePair(p) and
|
||||
optionsNode.flowsTo(arg) and
|
||||
disablingNode.asExpr().getExpr() = p
|
||||
disablingNode.asExpr() = p
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -88,10 +90,10 @@ private predicate isFalse(DataFlow::Node node) {
|
||||
/**
|
||||
* Holds if `p` is the pair `verify: false` or `verify_peer: false`.
|
||||
*/
|
||||
private predicate isVerifyFalsePair(Pair p) {
|
||||
private predicate isVerifyFalsePair(CfgNodes::ExprNodes::PairCfgNode p) {
|
||||
exists(DataFlow::Node key, DataFlow::Node value |
|
||||
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
value.asExpr() = p.getValue() and
|
||||
isVerifyLiteral(key) and
|
||||
isFalse(value)
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import ruby
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.frameworks.StandardLibrary
|
||||
@@ -32,8 +33,7 @@ class OpenUriRequest extends HTTP::Client::Request::Range {
|
||||
|
||||
override predicate disablesCertificateValidation(DataFlow::Node disablingNode) {
|
||||
exists(DataFlow::Node arg |
|
||||
arg.asExpr().getExpr() = requestUse.asExpr().getExpr().(MethodCall).getArgument(_)
|
||||
|
|
||||
arg.asExpr() = requestUse.asExpr().(CfgNodes::ExprNodes::MethodCallCfgNode).getAnArgument() and
|
||||
argumentDisablesValidation(arg, disablingNode)
|
||||
)
|
||||
}
|
||||
@@ -68,8 +68,7 @@ class OpenUriKernelOpenRequest extends HTTP::Client::Request::Range {
|
||||
override predicate disablesCertificateValidation(DataFlow::Node disablingNode) {
|
||||
exists(DataFlow::Node arg, int i |
|
||||
i > 0 and
|
||||
arg.asExpr().getExpr() = requestUse.asExpr().getExpr().(MethodCall).getArgument(i)
|
||||
|
|
||||
arg.asExpr() = requestUse.asExpr().(CfgNodes::ExprNodes::MethodCallCfgNode).getArgument(i) and
|
||||
argumentDisablesValidation(arg, disablingNode)
|
||||
)
|
||||
}
|
||||
@@ -85,24 +84,24 @@ class OpenUriKernelOpenRequest extends HTTP::Client::Request::Range {
|
||||
private predicate argumentDisablesValidation(DataFlow::Node arg, DataFlow::Node disablingNode) {
|
||||
// Either passed as an individual key:value argument, e.g.:
|
||||
// URI.open(..., ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
||||
isSslVerifyModeNonePair(arg.asExpr().getExpr()) and
|
||||
isSslVerifyModeNonePair(arg.asExpr()) and
|
||||
disablingNode = arg
|
||||
or
|
||||
// Or as a single hash argument, e.g.:
|
||||
// URI.open(..., { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE, ... })
|
||||
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
|
||||
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
|
||||
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
|
||||
isSslVerifyModeNonePair(p) and
|
||||
optionsNode.flowsTo(arg) and
|
||||
disablingNode.asExpr().getExpr() = p
|
||||
disablingNode.asExpr() = p
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `p` is the pair `ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE`. */
|
||||
private predicate isSslVerifyModeNonePair(Pair p) {
|
||||
private predicate isSslVerifyModeNonePair(CfgNodes::ExprNodes::PairCfgNode p) {
|
||||
exists(DataFlow::Node key, DataFlow::Node value |
|
||||
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
value.asExpr() = p.getValue() and
|
||||
isSslVerifyModeLiteral(key) and
|
||||
value = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").getAUse()
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import ruby
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
|
||||
@@ -50,16 +51,16 @@ class RestClientHttpRequest extends HTTP::Client::Request::Range {
|
||||
|
|
||||
// Either passed as an individual key:value argument, e.g.:
|
||||
// RestClient::Resource.new(..., verify_ssl: OpenSSL::SSL::VERIFY_NONE)
|
||||
isVerifySslNonePair(arg.asExpr().getExpr()) and
|
||||
isVerifySslNonePair(arg.asExpr()) and
|
||||
disablingNode = arg
|
||||
or
|
||||
// Or as a single hash argument, e.g.:
|
||||
// RestClient::Resource.new(..., { verify_ssl: OpenSSL::SSL::VERIFY_NONE })
|
||||
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
|
||||
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
|
||||
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
|
||||
isVerifySslNonePair(p) and
|
||||
optionsNode.flowsTo(arg) and
|
||||
disablingNode.asExpr().getExpr() = p
|
||||
disablingNode.asExpr() = p
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -68,10 +69,10 @@ class RestClientHttpRequest extends HTTP::Client::Request::Range {
|
||||
}
|
||||
|
||||
/** Holds if `p` is the pair `verify_ssl: OpenSSL::SSL::VERIFY_NONE`. */
|
||||
private predicate isVerifySslNonePair(Pair p) {
|
||||
private predicate isVerifySslNonePair(CfgNodes::ExprNodes::PairCfgNode p) {
|
||||
exists(DataFlow::Node key, DataFlow::Node value |
|
||||
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
value.asExpr() = p.getValue() and
|
||||
isSslVerifyModeLiteral(key) and
|
||||
value = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").getAUse()
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import ruby
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
|
||||
@@ -31,16 +32,16 @@ class TyphoeusHttpRequest extends HTTP::Client::Request::Range {
|
||||
|
|
||||
// Either passed as an individual key:value argument, e.g.:
|
||||
// Typhoeus.get(..., ssl_verifypeer: false)
|
||||
isSslVerifyPeerFalsePair(arg.asExpr().getExpr()) and
|
||||
isSslVerifyPeerFalsePair(arg.asExpr()) and
|
||||
disablingNode = arg
|
||||
or
|
||||
// Or as a single hash argument, e.g.:
|
||||
// Typhoeus.get(..., { ssl_verifypeer: false, ... })
|
||||
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
|
||||
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
|
||||
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
|
||||
isSslVerifyPeerFalsePair(p) and
|
||||
optionsNode.flowsTo(arg) and
|
||||
disablingNode.asExpr().getExpr() = p
|
||||
disablingNode.asExpr() = p
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -49,11 +50,10 @@ class TyphoeusHttpRequest extends HTTP::Client::Request::Range {
|
||||
}
|
||||
|
||||
/** Holds if `p` is the pair `ssl_verifypeer: false`. */
|
||||
private predicate isSslVerifyPeerFalsePair(Pair p) {
|
||||
private predicate isSslVerifyPeerFalsePair(CfgNodes::ExprNodes::PairCfgNode p) {
|
||||
exists(DataFlow::Node key, DataFlow::Node value |
|
||||
key.asExpr().getExpr() = p.getKey() and
|
||||
value.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
value.asExpr() = p.getValue() and
|
||||
isSslVerifyPeerLiteral(key) and
|
||||
isFalse(value)
|
||||
)
|
||||
|
||||
@@ -73,12 +73,12 @@ module UnsafeDeserialization {
|
||||
result = "object" and isSafe = false
|
||||
}
|
||||
|
||||
private predicate isOjModePair(Pair p, string modeValue) {
|
||||
private predicate isOjModePair(CfgNodes::ExprNodes::PairCfgNode p, string modeValue) {
|
||||
p.getKey().getConstantValue().isStringOrSymbol("mode") and
|
||||
exists(DataFlow::LocalSourceNode symbolLiteral, DataFlow::Node value |
|
||||
symbolLiteral.asExpr().getExpr().getConstantValue().isSymbol(modeValue) and
|
||||
symbolLiteral.flowsTo(value) and
|
||||
value.asExpr().getExpr() = p.getValue()
|
||||
value.asExpr() = p.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -91,7 +91,8 @@ module UnsafeDeserialization {
|
||||
OjOptionsHashWithModeKey() {
|
||||
exists(DataFlow::LocalSourceNode options |
|
||||
options.flowsTo(this) and
|
||||
isOjModePair(options.asExpr().getExpr().(HashLiteral).getAKeyValuePair(), modeValue)
|
||||
isOjModePair(options.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair(),
|
||||
modeValue)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -143,7 +144,7 @@ module UnsafeDeserialization {
|
||||
exists(DataFlow::Node arg, int i | i >= 1 and arg = this.getArgument(i) |
|
||||
arg.(OjOptionsHashWithModeKey).hasKnownMode(isSafe)
|
||||
or
|
||||
isOjModePair(arg.asExpr().getExpr(), getAKnownOjModeName(isSafe))
|
||||
isOjModePair(arg.asExpr(), getAKnownOjModeName(isSafe))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,19 @@ calls/calls.rb:
|
||||
# 230| getReceiver: [ConstantReadAccess] X
|
||||
# 229| getReceiver: [MethodCall] call to bar
|
||||
# 229| getReceiver: [ConstantReadAccess] X
|
||||
# 249| [HashLiteral] {...}
|
||||
# 249| getDesugared: [MethodCall] call to []
|
||||
# 249| getReceiver: [ConstantReadAccess] Hash
|
||||
# 249| getArgument: [Pair] Pair
|
||||
# 249| getKey: [MethodCall] call to foo
|
||||
# 249| getReceiver: [Self, SelfVariableAccess] self
|
||||
# 249| getValue: [MethodCall] call to bar
|
||||
# 249| getReceiver: [Self, SelfVariableAccess] self
|
||||
# 249| getArgument: [Pair] Pair
|
||||
# 249| getKey: [MethodCall] call to foo
|
||||
# 249| getReceiver: [ConstantReadAccess] X
|
||||
# 249| getValue: [MethodCall] call to bar
|
||||
# 249| getReceiver: [ConstantReadAccess] X
|
||||
# 314| [AssignExpr] ... = ...
|
||||
# 314| getDesugared: [StmtSequence] ...
|
||||
# 314| getStmt: [SetterMethodCall] call to foo=
|
||||
@@ -292,6 +305,13 @@ constants/constants.rb:
|
||||
# 20| getArgument: [StringLiteral] "Dave"
|
||||
# 20| getComponent: [StringTextComponent] Dave
|
||||
literals/literals.rb:
|
||||
# 88| [HashLiteral] {...}
|
||||
# 88| getDesugared: [MethodCall] call to []
|
||||
# 88| getReceiver: [ConstantReadAccess] Hash
|
||||
# 88| getArgument: [Pair] Pair
|
||||
# 88| getKey: [SymbolLiteral] :foo
|
||||
# 88| getValue: [StringLiteral] "bar"
|
||||
# 88| getComponent: [StringTextComponent] bar
|
||||
# 96| [ArrayLiteral] [...]
|
||||
# 96| getDesugared: [MethodCall] call to []
|
||||
# 96| getReceiver: [ConstantReadAccess] Array
|
||||
@@ -432,6 +452,31 @@ literals/literals.rb:
|
||||
# 113| getComponent: [StringTextComponent] #{BAR}
|
||||
# 113| getArgument: [SymbolLiteral] :"baz"
|
||||
# 113| getComponent: [StringTextComponent] baz
|
||||
# 116| [HashLiteral] {...}
|
||||
# 116| getDesugared: [MethodCall] call to []
|
||||
# 116| getReceiver: [ConstantReadAccess] Hash
|
||||
# 117| [HashLiteral] {...}
|
||||
# 117| getDesugared: [MethodCall] call to []
|
||||
# 117| getReceiver: [ConstantReadAccess] Hash
|
||||
# 117| getArgument: [Pair] Pair
|
||||
# 117| getKey: [SymbolLiteral] :foo
|
||||
# 117| getValue: [IntegerLiteral] 1
|
||||
# 117| getArgument: [Pair] Pair
|
||||
# 117| getKey: [SymbolLiteral] :bar
|
||||
# 117| getValue: [IntegerLiteral] 2
|
||||
# 117| getArgument: [Pair] Pair
|
||||
# 117| getKey: [StringLiteral] "baz"
|
||||
# 117| getComponent: [StringTextComponent] baz
|
||||
# 117| getValue: [IntegerLiteral] 3
|
||||
# 118| [HashLiteral] {...}
|
||||
# 118| getDesugared: [MethodCall] call to []
|
||||
# 118| getReceiver: [ConstantReadAccess] Hash
|
||||
# 118| getArgument: [Pair] Pair
|
||||
# 118| getKey: [SymbolLiteral] :foo
|
||||
# 118| getValue: [IntegerLiteral] 7
|
||||
# 118| getArgument: [HashSplatExpr] ** ...
|
||||
# 118| getAnOperand/getOperand/getReceiver: [MethodCall] call to baz
|
||||
# 118| getReceiver: [Self, SelfVariableAccess] self
|
||||
control/loops.rb:
|
||||
# 9| [ForExpr] for ... in ...
|
||||
# 9| getDesugared: [MethodCall] call to each
|
||||
@@ -511,12 +556,14 @@ control/loops.rb:
|
||||
# 24| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
|
||||
# 24| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
|
||||
# 22| getReceiver: [HashLiteral] {...}
|
||||
# 22| getElement: [Pair] Pair
|
||||
# 22| getKey: [SymbolLiteral] :foo
|
||||
# 22| getValue: [IntegerLiteral] 0
|
||||
# 22| getElement: [Pair] Pair
|
||||
# 22| getKey: [SymbolLiteral] :bar
|
||||
# 22| getValue: [IntegerLiteral] 1
|
||||
# 22| getDesugared: [MethodCall] call to []
|
||||
# 22| getReceiver: [ConstantReadAccess] Hash
|
||||
# 22| getArgument: [Pair] Pair
|
||||
# 22| getKey: [SymbolLiteral] :foo
|
||||
# 22| getValue: [IntegerLiteral] 0
|
||||
# 22| getArgument: [Pair] Pair
|
||||
# 22| getKey: [SymbolLiteral] :bar
|
||||
# 22| getValue: [IntegerLiteral] 1
|
||||
# 28| [ForExpr] for ... in ...
|
||||
# 28| getDesugared: [MethodCall] call to each
|
||||
# 28| getBlock: [BraceBlock] { ... }
|
||||
@@ -553,12 +600,14 @@ control/loops.rb:
|
||||
# 30| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
|
||||
# 31| getStmt: [BreakStmt] break
|
||||
# 28| getReceiver: [HashLiteral] {...}
|
||||
# 28| getElement: [Pair] Pair
|
||||
# 28| getKey: [SymbolLiteral] :foo
|
||||
# 28| getValue: [IntegerLiteral] 0
|
||||
# 28| getElement: [Pair] Pair
|
||||
# 28| getKey: [SymbolLiteral] :bar
|
||||
# 28| getValue: [IntegerLiteral] 1
|
||||
# 28| getDesugared: [MethodCall] call to []
|
||||
# 28| getReceiver: [ConstantReadAccess] Hash
|
||||
# 28| getArgument: [Pair] Pair
|
||||
# 28| getKey: [SymbolLiteral] :foo
|
||||
# 28| getValue: [IntegerLiteral] 0
|
||||
# 28| getArgument: [Pair] Pair
|
||||
# 28| getKey: [SymbolLiteral] :bar
|
||||
# 28| getValue: [IntegerLiteral] 1
|
||||
# 36| [AssignAddExpr] ... += ...
|
||||
# 36| getDesugared: [AssignExpr] ... = ...
|
||||
# 36| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
@@ -624,6 +673,15 @@ operations/operations.rb:
|
||||
# 29| getDesugared: [MethodCall] call to []
|
||||
# 29| getReceiver: [ConstantReadAccess] Array
|
||||
# 29| getArgument: [IntegerLiteral] 2
|
||||
# 29| [HashLiteral] {...}
|
||||
# 29| getDesugared: [MethodCall] call to []
|
||||
# 29| getReceiver: [ConstantReadAccess] Hash
|
||||
# 29| getArgument: [Pair] Pair
|
||||
# 29| getKey: [SymbolLiteral] :b
|
||||
# 29| getValue: [IntegerLiteral] 4
|
||||
# 29| getArgument: [Pair] Pair
|
||||
# 29| getKey: [SymbolLiteral] :c
|
||||
# 29| getValue: [IntegerLiteral] 5
|
||||
# 69| [AssignAddExpr] ... += ...
|
||||
# 69| getDesugared: [AssignExpr] ... = ...
|
||||
# 69| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
@@ -721,6 +779,9 @@ operations/operations.rb:
|
||||
# 96| getAnOperand/getLeftOperand/getReceiver: [GlobalVariableAccess] $global_var
|
||||
# 96| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 6
|
||||
params/params.rb:
|
||||
# 8| [HashLiteral] {...}
|
||||
# 8| getDesugared: [MethodCall] call to []
|
||||
# 8| getReceiver: [ConstantReadAccess] Hash
|
||||
# 21| [ArrayLiteral] [...]
|
||||
# 21| getDesugared: [MethodCall] call to []
|
||||
# 21| getReceiver: [ConstantReadAccess] Array
|
||||
|
||||
@@ -11,6 +11,8 @@ hashSplatExpr
|
||||
| calls.rb:274:5:274:9 | ** ... | calls.rb:274:7:274:9 | call to bar |
|
||||
| calls.rb:275:5:275:12 | ** ... | calls.rb:275:7:275:12 | call to bar |
|
||||
keywordArguments
|
||||
| calls.rb:249:3:249:12 | Pair | calls.rb:249:3:249:5 | call to foo | calls.rb:249:10:249:12 | call to bar |
|
||||
| calls.rb:249:15:249:30 | Pair | calls.rb:249:15:249:20 | call to foo | calls.rb:249:25:249:30 | call to bar |
|
||||
| calls.rb:278:5:278:13 | Pair | calls.rb:278:5:278:8 | :blah | calls.rb:278:11:278:13 | call to bar |
|
||||
| calls.rb:279:5:279:16 | Pair | calls.rb:279:5:279:8 | :blah | calls.rb:279:11:279:16 | call to bar |
|
||||
keywordArgumentsByKeyword
|
||||
|
||||
@@ -22,6 +22,8 @@ callsWithArguments
|
||||
| calls.rb:85:1:85:12 | ... + ... | + | 0 | calls.rb:85:7:85:12 | call to bar |
|
||||
| calls.rb:234:1:234:8 | ...[...] | [] | 0 | calls.rb:234:5:234:7 | call to bar |
|
||||
| calls.rb:235:1:235:14 | ...[...] | [] | 0 | calls.rb:235:8:235:13 | call to bar |
|
||||
| calls.rb:249:1:249:32 | call to [] | [] | 0 | calls.rb:249:3:249:12 | Pair |
|
||||
| calls.rb:249:1:249:32 | call to [] | [] | 1 | calls.rb:249:15:249:30 | Pair |
|
||||
| calls.rb:266:1:266:9 | call to foo | foo | 0 | calls.rb:266:5:266:8 | &... |
|
||||
| calls.rb:267:1:267:12 | call to foo | foo | 0 | calls.rb:267:5:267:11 | &... |
|
||||
| calls.rb:270:1:270:9 | call to foo | foo | 0 | calls.rb:270:5:270:8 | * ... |
|
||||
@@ -248,6 +250,7 @@ callsWithReceiver
|
||||
| calls.rb:245:6:245:8 | call to bar | calls.rb:245:6:245:8 | self |
|
||||
| calls.rb:246:1:246:6 | call to foo | calls.rb:246:1:246:1 | X |
|
||||
| calls.rb:246:9:246:14 | call to bar | calls.rb:246:9:246:9 | X |
|
||||
| calls.rb:249:1:249:32 | call to [] | calls.rb:249:1:249:32 | Hash |
|
||||
| calls.rb:249:3:249:5 | call to foo | calls.rb:249:3:249:5 | self |
|
||||
| calls.rb:249:10:249:12 | call to bar | calls.rb:249:10:249:12 | self |
|
||||
| calls.rb:249:15:249:20 | call to foo | calls.rb:249:15:249:15 | X |
|
||||
|
||||
@@ -2434,11 +2434,14 @@ cfg.rb:
|
||||
#-----| -> map2
|
||||
|
||||
# 97| map1
|
||||
#-----| -> a
|
||||
#-----| -> Hash
|
||||
|
||||
# 97| {...}
|
||||
# 97| call to []
|
||||
#-----| -> ... = ...
|
||||
|
||||
# 97| Hash
|
||||
#-----| -> a
|
||||
|
||||
# 97| Pair
|
||||
#-----| -> c
|
||||
|
||||
@@ -2473,7 +2476,7 @@ cfg.rb:
|
||||
#-----| -> f
|
||||
|
||||
# 97| Pair
|
||||
#-----| -> {...}
|
||||
#-----| -> call to []
|
||||
|
||||
# 97| "f"
|
||||
#-----| -> Pair
|
||||
@@ -2485,11 +2488,14 @@ cfg.rb:
|
||||
#-----| -> parameters
|
||||
|
||||
# 98| map2
|
||||
#-----| -> map1
|
||||
#-----| -> Hash
|
||||
|
||||
# 98| {...}
|
||||
# 98| call to []
|
||||
#-----| -> ... = ...
|
||||
|
||||
# 98| Hash
|
||||
#-----| -> map1
|
||||
|
||||
# 98| ** ...
|
||||
#-----| -> x
|
||||
|
||||
@@ -2512,7 +2518,7 @@ cfg.rb:
|
||||
#-----| -> "y"
|
||||
|
||||
# 98| ** ...
|
||||
#-----| -> {...}
|
||||
#-----| -> call to []
|
||||
|
||||
# 98| map1
|
||||
#-----| -> ** ...
|
||||
|
||||
@@ -148,6 +148,12 @@ positionalArguments
|
||||
| cfg.rb:90:10:90:26 | call to [] | cfg.rb:90:21:90:25 | 3.4e5 |
|
||||
| cfg.rb:91:6:91:10 | ... > ... | cfg.rb:91:10:91:10 | 3 |
|
||||
| cfg.rb:92:3:92:8 | call to puts | cfg.rb:92:8:92:8 | x |
|
||||
| cfg.rb:97:8:97:39 | call to [] | cfg.rb:97:10:97:19 | Pair |
|
||||
| cfg.rb:97:8:97:39 | call to [] | cfg.rb:97:22:97:29 | Pair |
|
||||
| cfg.rb:97:8:97:39 | call to [] | cfg.rb:97:32:97:37 | Pair |
|
||||
| cfg.rb:98:8:98:36 | call to [] | cfg.rb:98:10:98:15 | ** ... |
|
||||
| cfg.rb:98:8:98:36 | call to [] | cfg.rb:98:18:98:27 | Pair |
|
||||
| cfg.rb:98:8:98:36 | call to [] | cfg.rb:98:30:98:35 | ** ... |
|
||||
| cfg.rb:102:3:102:12 | call to puts | cfg.rb:102:8:102:12 | value |
|
||||
| cfg.rb:103:10:103:20 | ...[...] | cfg.rb:103:17:103:19 | key |
|
||||
| cfg.rb:108:1:108:12 | call to puts | cfg.rb:108:6:108:12 | ( ... ) |
|
||||
@@ -344,3 +350,4 @@ positionalArguments
|
||||
keywordArguments
|
||||
| cfg.html.erb:6:9:6:58 | call to stylesheet_link_tag | media | cfg.html.erb:6:54:6:58 | "all" |
|
||||
| cfg.html.erb:12:11:12:33 | call to link_to | id | cfg.html.erb:12:31:12:33 | "a" |
|
||||
| cfg.rb:97:8:97:39 | call to [] | e | cfg.rb:97:35:97:37 | "f" |
|
||||
|
||||
Reference in New Issue
Block a user