mirror of
https://github.com/github/codeql.git
synced 2026-04-25 08:45:14 +02:00
Add query rb/unsafe-deserialization
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for reasoning about unsafe
|
||||
* deserialization, as well as extension points for adding your own.
|
||||
*/
|
||||
|
||||
private import ruby
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.dataflow.RemoteFlowSources
|
||||
|
||||
module UnsafeDeserialization {
|
||||
/**
|
||||
* A data flow source for unsafe deserialization vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for unsafe deserialization vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for unsafe deserialization vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/** A source of remote user input, considered as a flow source for unsafe deserialization. */
|
||||
class RemoteFlowSourceAsSource extends Source {
|
||||
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument in a call to `Marshal.load` or `Marshal.restore`, considered a
|
||||
* sink for unsafe deserialization.
|
||||
*/
|
||||
class MarshalLoadOrRestoreArgument extends Sink {
|
||||
MarshalLoadOrRestoreArgument() {
|
||||
this = API::getTopLevelMember("Marshal").getAMethodCall(["load", "restore"]).getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument in a call to `YAML.load`, considered a sink for unsafe
|
||||
* deserialization.
|
||||
*/
|
||||
class YamlLoadArgument extends Sink {
|
||||
YamlLoadArgument() {
|
||||
this = API::getTopLevelMember("YAML").getAMethodCall("load").getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument in a call to `JSON.load` or `JSON.restore`, considered a sink
|
||||
* for unsafe deserialization.
|
||||
*/
|
||||
class JsonLoadArgument extends Sink {
|
||||
JsonLoadArgument() {
|
||||
this = API::getTopLevelMember("JSON").getAMethodCall(["load", "restore"]).getArgument(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
30
ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll
Normal file
30
ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for reasoning about unsafe deserialization.
|
||||
*
|
||||
* Note, for performance reasons: only import this file if
|
||||
* `UnsafeDeserialization::Configuration` is needed, otherwise
|
||||
* `UnsafeDeserializationCustomizations` should be imported instead.
|
||||
*/
|
||||
|
||||
private import ruby
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.TaintTracking
|
||||
import UnsafeDeserializationCustomizations
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about unsafe deserialization.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "UnsafeDeserialization" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof UnsafeDeserialization::Source
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeDeserialization::Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
super.isSanitizer(node) or
|
||||
node instanceof UnsafeDeserialization::Sanitizer
|
||||
}
|
||||
}
|
||||
56
ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
Normal file
56
ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
Normal file
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Deserializing 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.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Avoid deserialization of untrusted data if possible. If the architecture permits
|
||||
it, use serialization formats that cannot represent arbitarary objects. For
|
||||
libraries that support it, such as the Ruby standard library's <code>JSON</code>
|
||||
module, ensure that the parser is configured to disable
|
||||
deserialization of arbitrary objects.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example calls the <code>Marshal.load</code>, <code>JSON.load</code>, and
|
||||
<code>YAML.load</code> methods on data from an HTTP request. Since these methods
|
||||
are capable of deserializing to arbitrary objects, this is inherently unsafe.
|
||||
</p>
|
||||
<sample src="examples/UnsafeDeserializationBad.rb"/>
|
||||
<p>
|
||||
Using <code>YAML.parse</code> instead, with the default options, removes the
|
||||
vulnerability.
|
||||
</p>
|
||||
<sample src="examples/UnsafeDeserializationGood.rb"/>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
|
||||
<li>
|
||||
OWASP vulnerability description:
|
||||
<a href="https://www.owasp.org/index.php/Deserialization_of_untrusted_data">deserialization of untrusted data</a>.
|
||||
</li>
|
||||
<li>
|
||||
Ruby documentation: <a href="https://docs.ruby-lang.org/en/3.0.0/doc/security_rdoc.html">guidance on deserializing objects safely</a>.
|
||||
</li>
|
||||
<li>
|
||||
Ruby documentation: <a href="https://ruby-doc.org/core-3.0.2/Marshal.html#module-Marshal-label-Security+considerations">security guidance on the Marshal library</a>.
|
||||
</li>
|
||||
<li>
|
||||
Ruby documentation: <a href="https://ruby-doc.org/stdlib-3.0.2/libdoc/json/rdoc/JSON.html#method-i-load">security guidance on JSON.load</a>.
|
||||
</li>
|
||||
<li>
|
||||
Ruby documentation: <a href="https://ruby-doc.org/stdlib-3.0.2/libdoc/yaml/rdoc/YAML.html#module-YAML-label-Security">security guidance on the YAML library</a>.
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
21
ql/src/queries/security/cwe-502/UnsafeDeserialization.ql
Normal file
21
ql/src/queries/security/cwe-502/UnsafeDeserialization.ql
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @name Deserialization of user-controlled data
|
||||
* @description Deserializing user-controlled data may allow attackers to
|
||||
* execute arbitrary code.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 9.8
|
||||
* @precision high
|
||||
* @id rb/unsafe-deserialization
|
||||
* @tags security
|
||||
* external/cwe/cwe-502
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import DataFlow::PathGraph
|
||||
import codeql.ruby.DataFlow
|
||||
import codeql.ruby.security.UnsafeDeserializationQuery
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Unsafe deserialization of $@.", source.getNode(), "user input"
|
||||
@@ -0,0 +1,20 @@
|
||||
require 'json'
|
||||
require 'yaml'
|
||||
|
||||
class UserController < ActionController::Base
|
||||
def marshal_example
|
||||
data = Base64.decode64 params[:data]
|
||||
object = Marshal.load data
|
||||
# ...
|
||||
end
|
||||
|
||||
def json_example
|
||||
object = JSON.load params[:json]
|
||||
# ...
|
||||
end
|
||||
|
||||
def yaml_example
|
||||
object = YAML.load params[:yaml]
|
||||
# ...
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
require 'json'
|
||||
|
||||
class UserController < ActionController::Base
|
||||
def safe_json_example
|
||||
object = JSON.parse params[:json]
|
||||
# ...
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user