make seperate steps for YAML.parse* and use getAsuccessor*() to reach final to_ruby method call, All parts have Rewritten with API graphs exclusively

This commit is contained in:
amammad
2023-06-12 20:06:26 +10:00
committed by Harry Maclean
parent c22cbf5b01
commit 1410574f76
4 changed files with 66 additions and 13 deletions

View File

@@ -10,25 +10,48 @@ private import codeql.ruby.ApiGraphs
* A taint step related to the result of `YAML.parse` calls, or similar.
* In the following example, this step will propagate taint from
* `source` to `sink`:
*
* this contains two seperate steps:
* ```rb
* x = source
* result = YAML.parse(x)
* sink result.to_ruby # Unsafe call
* sink = YAML.parse(x)
* ```
* By second step
* source is a Successor of `YAML.parse(x)`
* which ends with `to_ruby` or an Element of `to_ruby`
* ```ruby
* sink source.to_ruby # Unsafe call
* ```
*/
private class YamlParseStep extends AdditionalTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode yamlParserMethod |
succ = yamlParserMethod.getAMethodCall("to_ruby") and
exists(API::Node yamlParserMethod |
succ = yamlParserMethod.getReturn().asSource() and
(
yamlParserMethod = yamlNode().getAMethodCall(["parse", "parse_stream"]) and
pred = [yamlParserMethod.getArgument(0), yamlParserMethod.getKeywordArgument("yaml")]
yamlParserMethod = yamlNode().getMethod(["parse", "parse_stream"]) and
pred =
[yamlParserMethod.getParameter(0), yamlParserMethod.getKeywordParameter("yaml")].asSink()
or
yamlParserMethod = yamlNode().getAMethodCall("parse_file") and
pred = [yamlParserMethod.getArgument(0), yamlParserMethod.getKeywordArgument("filename")]
yamlParserMethod = yamlNode().getMethod("parse_file") and
pred =
[yamlParserMethod.getParameter(0), yamlParserMethod.getKeywordParameter("filename")]
.asSink()
)
)
or
exists(API::Node yamlParserMethod |
succ =
[
yamlParserMethod.getASuccessor*().getMethod("to_ruby").getReturn().asSource(),
yamlParserMethod
.getASuccessor*()
.getMethod("to_ruby")
.getReturn()
.getAnElement()
.asSource()
] and
yamlParserMethod = yamlNode().getMethod(["parse", "parse_stream", "parse_file"]) and
pred = yamlParserMethod.getReturn().asSource()
)
}
}

View File

@@ -97,12 +97,26 @@ module UnsafeDeserialization {
/**
* An argument in a call to `YAML.parse*`, considered a sink for unsafe deserialization
* if there is a call to `to_ruby` on the returned value.
* if there is a call to `to_ruby` on the returned value of any Successor.
*/
class YamlParseArgument extends Sink {
YamlParseArgument() {
this =
yamlNode().getAMethodCall(["parse", "parse_stream", "parse_file"]).getAMethodCall("to_ruby")
[
yamlNode()
.getMethod(["parse", "parse_stream", "parse_file"])
.getASuccessor*()
.getMethod("to_ruby")
.getReturn()
.asSource(),
yamlNode()
.getMethod(["parse", "parse_stream", "parse_file"])
.getASuccessor*()
.getMethod("to_ruby")
.getReturn()
.getAnElement()
.asSource()
]
}
}

View File

@@ -1 +1 @@
queries/security/cwe-502/UnsafeDeserialization.ql
queries/security/cwe-502/UnsafeDeserialization.ql

View File

@@ -15,7 +15,23 @@ class UsersController < ActionController::Base
parse_output.to_ruby
Psych.parse(params[:yaml_string]).to_ruby
Psych.parse_file(params[:yaml_file]).to_ruby
parsed_yaml.children.each do |child|
puts child.to_ruby
end
Psych.parse_stream(params[:yaml_string]) do |document|
puts document.to_ruby
end
parsed_yaml.children.first.to_ruby
parsed_yaml = Psych.parse_stream(params[:yaml_string])
content = parsed_yaml.children[0].children[0].children
parsed = parsed_yaml.to_ruby[0]
parsed = content.to_ruby[0]
Psych.parse(params[:yaml_string]).children[0].to_ruby
# FP
parsed_yaml = Psych2.parse_stream(params[:yaml_string])
content = parsed_yaml.children[0].children[0].children
parsed = parsed_yaml.to_ruby
parsed = parsed_yaml.to_ruby[0]
end
end