-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Ruby: add seperate additional steps between YAML.parse*
methods and to_ruby
#13431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
You mentioned in the PR description that there are many ways of accessing children of a YAML node. If you don't want to precisely model which methods are doing what, you could do something like: API::Node yamlChildNodeAccess(API::Node source) {
source = <return value from initial parse call>
or
result = yamlChildNodeAccess(source).getMethod(_).getReturn()
or
result = yamlChildNodeAccess(source).getMethod(_).getBlock().getParameter(_)
or
result = yamlChildNodeAccess(source).getAnElement()
} |
Thanks @asgerf
API::Node yamlChildNodeAccess(API::Node source) {
source = yamlNode().getMethod(["parse", "parse_stream"]).getReturn()
or
result = yamlChildNodeAccess(source).getMethod(_).getReturn()
or
result = yamlChildNodeAccess(source).getMethod(_).getBlock().getParameter(_)
or
result = yamlChildNodeAccess(source).getAnElement()
}
|
well,I think I was wrong, now it is working for me, exists(API::Node parseSuccessors | parseSuccessors = yamlChildNodeAccess(_) |
succ =
[
parseSuccessors.getMethod("to_ruby").getReturn().asSource(),
parseSuccessors.getMethod("to_ruby").getReturn().getAnElement().asSource()
] and
pred = parseSuccessors.asSource()
) |
Sorry, the base case needs
Eventually it will be deprecated in JS as well but it will be a while and we won't reject submissions that occurred before deprecation. |
API::Node yamlParseChildNodeAccess(API::Node source) {
source = yamlNode().getMethod(["parse", "parse_stream"]).getReturn() and source = result
or
(
result = yamlParseChildNodeAccess(source).getMethod(_).getReturn()
or
result = yamlParseChildNodeAccess(source).getMethod(_).getBlock().getParameter(_)
or
result = yamlParseChildNodeAccess(source).getAnElement()
) and
result.getPath().matches(yamlNode().getMethod(["parse", "parse_stream"]).getPath() + "%")
}
|
About 1 I was wrong, sorry to took your time! |
getAsuccessor*()
to rea…getAsuccessor*()
to rea…
getAsuccessor*()
to rea…YAML.parse*
methods and to_ruby
ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
Outdated
Show resolved
Hide resolved
Hi @pwntester |
Looks great, please fix the tests so we can merge this. |
ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.rb
Outdated
Show resolved
Hide resolved
} | ||
} | ||
|
||
predicate isAdditionalFlowStepTest(DataFlow::Node pred, DataFlow::Node succ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hmac This predicate is the same as above just for testing, so the predicate works fine and I can see additional steps from the first argument of a yaml parse method like YAML.parse(arg)
to a to_ruby
method return value. but I don't know why it doesn't work in my StateConfigSig
implementation.
Hi, @calumgrant the query is ready for review. |
…ch final to_ruby method call, All parts have Rewritten with API graphs exclusively
… and thank him that providing the solution
…mental query only for yaml
251fb99
to
32f5667
Compare
QHelp previews: ruby/ql/src/experimental/cwe-502/UnsafeYamlDeserialization.qhelpDeserialization of user-controlled yaml dataDeserializing untrusted data using any method that allows the construction of arbitrary objects is easily exploitable and, in many cases, allows an attacker to execute arbitrary code. RecommendationIf deserializing an untrusted YAML document using the ExampleThe following example calls the require 'yaml'
class UserController < ActionController::Base
def yaml_example
object = YAML.unsafe_load params[:yaml]
object = YAML.load_stream params[:yaml]
parsed_yaml = Psych.parse_stream(params[:yaml])
# to_ruby is unsafe
parsed_yaml.children.each do |child|
object = child.to_ruby
end
object = Psych.parse(params[:yaml]).to_ruby
# ...
end
end Using require 'yaml'
class UserController < ActionController::Base
def safe_yaml_example
object = YAML.load params[:yaml]
object = Psych.load_file params[:yaml]
object = YAML.safe_load params[:yaml]
# ...
end
end References
|
/** | ||
* 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 | ||
* 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 | ||
* ``` | ||
*/ |
Check warning
Code scanning / CodeQL
Misspelling
/** | ||
* 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 | ||
* 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 | ||
* ``` | ||
*/ |
Check warning
Code scanning / CodeQL
Predicate QLDoc style.
related to this.
the changes summary:
DataFlow::CallNode
).getASuccessor*()
before finding theto_ruby
method, there are a lot of possibilities betweenparse/parse_stream/...
methods andto_ruby
like block parameters, elements of returned arrays.new examples that pushed me to improve the query:
Also, It is really important to me ( more than my query submission to codeql BB program) to get feedback about using
getASuccessor*()
in case of performance and accuracy if there is only an unique end for Successors like here that isto_ruby
, additionally I'm in the process to submit multiple queries in this week so it would be great to give me a hint about this and I'll submit another version of my queries with this approach too.