Merge branch 'main' into redsun82/cmake-generator-prototype

This commit is contained in:
Paolo Tranquilli
2022-10-14 10:13:12 +02:00
68 changed files with 406 additions and 240 deletions

View File

@@ -885,7 +885,7 @@ module TestOutput {
/**
* Gets a string used to resolve ties in node and edge ordering.
*/
string getOrderDisambuigation() { result = "" }
string getOrderDisambiguation() { result = "" }
}
query predicate nodes(RelevantNode n, string attr, string val) {
@@ -900,7 +900,7 @@ module TestOutput {
order by
l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(),
l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString(),
p.getOrderDisambuigation()
p.getOrderDisambiguation()
)
).toString()
}
@@ -923,7 +923,7 @@ module TestOutput {
order by
l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(),
l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString(),
s.getOrderDisambuigation()
s.getOrderDisambiguation()
)
).toString()
}

View File

@@ -202,7 +202,7 @@ private predicate isFork(State q, InputSymbol s1, InputSymbol s2, State r1, Stat
//
// We additionally require that the there exists another InfiniteRepetitionQuantifier `mid` on the path from `q` to itself.
// This is done to avoid flagging regular expressions such as `/(a?)*b/` - that only has polynomial runtime, and is detected by `js/polynomial-redos`.
// The below code is therefore a heuritic, that only flags regular expressions such as `/(a*)*b/`,
// The below code is therefore a heuristic, that only flags regular expressions such as `/(a*)*b/`,
// and does not flag regular expressions such as `/(a?b?)c/`, but the latter pattern is not used frequently.
r1 = r2 and
q1 = q2 and

View File

@@ -76,7 +76,7 @@ class StateTuple extends TStateTuple {
StateTuple() { this = MkStateTuple(q1, q2, q3) }
/**
* Gest a string repesentation of this tuple.
* Gest a string representation of this tuple.
*/
string toString() { result = "(" + q1 + ", " + q2 + ", " + q3 + ")" }

View File

@@ -1,5 +1,5 @@
/**
* Provides precicates for reasoning about bad tag filter vulnerabilities.
* Provides predicates for reasoning about bad tag filter vulnerabilities.
*/
import regexp.RegexpMatching
@@ -65,7 +65,7 @@ predicate isBadRegexpFilter(HtmlMatchingRegExp regexp, string msg) {
regexp.matches("<!-- foo --!>") and
exists(int a, int b | a != b |
regexp.fillsCaptureGroup("<!-- foo -->", a) and
// <!-- foo --> might be ambigously parsed (matching both capture groups), and that is ok here.
// <!-- foo --> might be ambiguously parsed (matching both capture groups), and that is ok here.
regexp.fillsCaptureGroup("<!-- foo --!>", b) and
not regexp.fillsCaptureGroup("<!-- foo --!>", a) and
msg =

View File

@@ -202,7 +202,7 @@ private predicate isFork(State q, InputSymbol s1, InputSymbol s2, State r1, Stat
//
// We additionally require that the there exists another InfiniteRepetitionQuantifier `mid` on the path from `q` to itself.
// This is done to avoid flagging regular expressions such as `/(a?)*b/` - that only has polynomial runtime, and is detected by `js/polynomial-redos`.
// The below code is therefore a heuritic, that only flags regular expressions such as `/(a*)*b/`,
// The below code is therefore a heuristic, that only flags regular expressions such as `/(a*)*b/`,
// and does not flag regular expressions such as `/(a?b?)c/`, but the latter pattern is not used frequently.
r1 = r2 and
q1 = q2 and

View File

@@ -1,5 +1,5 @@
/**
* Provides precicates for reasoning about which strings are matched by a regular expression,
* Provides predicates for reasoning about which strings are matched by a regular expression,
* and for testing which capture groups are filled when a particular regexp matches a string.
*/

View File

@@ -76,7 +76,7 @@ class StateTuple extends TStateTuple {
StateTuple() { this = MkStateTuple(q1, q2, q3) }
/**
* Gest a string repesentation of this tuple.
* Gest a string representation of this tuple.
*/
string toString() { result = "(" + q1 + ", " + q2 + ", " + q3 + ")" }

View File

@@ -1,5 +1,5 @@
/**
* Provides precicates for reasoning about bad tag filter vulnerabilities.
* Provides predicates for reasoning about bad tag filter vulnerabilities.
*/
import regexp.RegexpMatching
@@ -65,7 +65,7 @@ predicate isBadRegexpFilter(HtmlMatchingRegExp regexp, string msg) {
regexp.matches("<!-- foo --!>") and
exists(int a, int b | a != b |
regexp.fillsCaptureGroup("<!-- foo -->", a) and
// <!-- foo --> might be ambigously parsed (matching both capture groups), and that is ok here.
// <!-- foo --> might be ambiguously parsed (matching both capture groups), and that is ok here.
regexp.fillsCaptureGroup("<!-- foo --!>", b) and
not regexp.fillsCaptureGroup("<!-- foo --!>", a) and
msg =

View File

@@ -202,7 +202,7 @@ private predicate isFork(State q, InputSymbol s1, InputSymbol s2, State r1, Stat
//
// We additionally require that the there exists another InfiniteRepetitionQuantifier `mid` on the path from `q` to itself.
// This is done to avoid flagging regular expressions such as `/(a?)*b/` - that only has polynomial runtime, and is detected by `js/polynomial-redos`.
// The below code is therefore a heuritic, that only flags regular expressions such as `/(a*)*b/`,
// The below code is therefore a heuristic, that only flags regular expressions such as `/(a*)*b/`,
// and does not flag regular expressions such as `/(a?b?)c/`, but the latter pattern is not used frequently.
r1 = r2 and
q1 = q2 and

View File

@@ -1,5 +1,5 @@
/**
* Provides precicates for reasoning about which strings are matched by a regular expression,
* Provides predicates for reasoning about which strings are matched by a regular expression,
* and for testing which capture groups are filled when a particular regexp matches a string.
*/

View File

@@ -76,7 +76,7 @@ class StateTuple extends TStateTuple {
StateTuple() { this = MkStateTuple(q1, q2, q3) }
/**
* Gest a string repesentation of this tuple.
* Gest a string representation of this tuple.
*/
string toString() { result = "(" + q1 + ", " + q2 + ", " + q3 + ")" }

View File

@@ -216,7 +216,7 @@ struct Visitor<'a> {
schema: &'a NodeTypeMap,
/// A stack for gathering information from child nodes. Whenever a node is
/// entered the parent's [Label], child counter, and an empty list is pushed.
/// All children append their data to the the list. When the visitor leaves a
/// All children append their data to the list. When the visitor leaves a
/// node the list containing the child data is popped from the stack and
/// matched against the dbscheme for the node. If the expectations are met
/// the corresponding row definitions are added to the trap_output.

View File

@@ -43,7 +43,7 @@ pub enum FieldTypeInfo {
},
/// The field can be one of several tokens, so the db type will be an `int`
/// with a `case @foo.kind` for each possiblity.
/// with a `case @foo.kind` for each possibility.
ReservedWordInt(BTreeMap<String, (usize, String)>),
}

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* `ActiveJob::Serializers.deserialize` is considered to be a code execution sink.

View File

@@ -898,7 +898,7 @@ module API {
/** Gets the `subclass` edge label. */
LabelSubclass subclass() { any() }
/** Gets the label representing the given keword argument/parameter. */
/** Gets the label representing the given keyword argument/parameter. */
LabelKeywordParameter keywordParameter(string name) { result.getName() = name }
/** Gets the label representing the `n`th positional argument/parameter. */

View File

@@ -5,6 +5,7 @@
private import codeql.ruby.frameworks.Core
private import codeql.ruby.frameworks.ActionCable
private import codeql.ruby.frameworks.ActionController
private import codeql.ruby.frameworks.ActiveJob
private import codeql.ruby.frameworks.ActionMailer
private import codeql.ruby.frameworks.ActiveRecord
private import codeql.ruby.frameworks.ActiveResource

View File

@@ -106,7 +106,7 @@ class MethodCall extends Call instanceof MethodCallImpl {
final Block getBlock() { result = super.getBlockImpl() }
/**
* Holds if the safe nagivation operator (`&.`) is used in this call.
* Holds if the safe navigation operator (`&.`) is used in this call.
* ```rb
* foo&.empty?
* ```

View File

@@ -65,7 +65,7 @@ class ConstantValue extends TConstantValue {
/** Holds if this is the string value `s`. */
predicate isString(string s) { s = this.getString() }
/** Gets the symbol value (exluding the `:` prefix), if this is a symbol. */
/** Gets the symbol value (excluding the `:` prefix), if this is a symbol. */
string getSymbol() { this = TSymbol(result) }
/** Holds if this is the symbol value `:s`. */

View File

@@ -394,7 +394,7 @@ private module ResolveImpl {
/**
* The qualified names of the ancestors of a class/module. The ancestors should be an ordered list
* of the ancestores of `prepend`ed modules, the module itself , the ancestors or `include`d modules
* of the ancestors of `prepend`ed modules, the module itself , the ancestors or `include`d modules
* and the ancestors of the super class. The priority value only distinguishes the kind of ancestor,
* it does not order the ancestors within a group of the same kind. This is an over-approximation, however,
* computing the precise order is tricky because it depends on the evaluation/file loading order.

View File

@@ -885,7 +885,7 @@ module TestOutput {
/**
* Gets a string used to resolve ties in node and edge ordering.
*/
string getOrderDisambuigation() { result = "" }
string getOrderDisambiguation() { result = "" }
}
query predicate nodes(RelevantNode n, string attr, string val) {
@@ -900,7 +900,7 @@ module TestOutput {
order by
l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(),
l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString(),
p.getOrderDisambuigation()
p.getOrderDisambiguation()
)
).toString()
}
@@ -923,7 +923,7 @@ module TestOutput {
order by
l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(),
l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString(),
s.getOrderDisambuigation()
s.getOrderDisambiguation()
)
).toString()
}

View File

@@ -46,7 +46,7 @@ module SummaryComponent {
/**
* Gets a summary component that represents an element in a collection at a specific
* known index `cv`, or an uknown index.
* known index `cv`, or an unknown index.
*/
SummaryComponent elementKnownOrUnknown(ConstantValue cv) {
result = SC::content(TKnownOrUnknownElementContent(TKnownElementContent(cv)))

View File

@@ -1,5 +1,5 @@
/**
* Provides an extension point for for modeling user-controlled data.
* Provides an extension point for modeling user-controlled data.
* Such data is often used as data-flow sources in security queries.
*/

View File

@@ -1165,8 +1165,8 @@ private module PostUpdateNodes {
ExprPostUpdateNode() { this = TExprPostUpdateNode(e) }
override ExprNode getPreUpdateNode() {
// For compund arguments, such as `m(if b then x else y)`, we want the leaf nodes
// `[post] x` and `[post] y` to have two pre-update nodes: (1) the compund argument,
// For compound arguments, such as `m(if b then x else y)`, we want the leaf nodes
// `[post] x` and `[post] y` to have two pre-update nodes: (1) the compound argument,
// `if b then x else y`; and the (2) the underlying expressions; `x` and `y`,
// respectively.
//

View File

@@ -64,7 +64,7 @@ predicate uninitializedWrite(Cfg::EntryBasicBlock bb, int i, LocalVariable v) {
i = -1
}
/** Holds if `bb` contains a caputured read of variable `v`. */
/** Holds if `bb` contains a captured read of variable `v`. */
pragma[noinline]
private predicate hasCapturedVariableRead(Cfg::BasicBlock bb, LocalVariable v) {
exists(LocalVariableReadAccess read |
@@ -74,7 +74,7 @@ private predicate hasCapturedVariableRead(Cfg::BasicBlock bb, LocalVariable v) {
)
}
/** Holds if `bb` contains a caputured write to variable `v`. */
/** Holds if `bb` contains a captured write to variable `v`. */
pragma[noinline]
private predicate writesCapturedVariable(Cfg::BasicBlock bb, LocalVariable v) {
exists(LocalVariableWriteAccess write |

View File

@@ -417,7 +417,7 @@ module Rbi {
override ReturnType getReturnType() { result = ReturnsCall.super.getReturnType() }
}
/** A call to `void` that spcifies that a given method does not return a useful value. */
/** A call to `void` that specifies that a given method does not return a useful value. */
class MethodVoidCall extends MethodReturnsTypeCall instanceof VoidCall {
override ReturnType getReturnType() { result = VoidCall.super.getReturnType() }
}
@@ -448,7 +448,7 @@ module Rbi {
}
/**
* A call to `void` that spcifies that a given proc or block does not return
* A call to `void` that specifies that a given proc or block does not return
* a useful value.
*/
class ProcVoidCall extends ProcReturnsTypeCall instanceof VoidCall {

View File

@@ -6,7 +6,7 @@ private import codeql.ruby.ast.internal.TreeSitter
/** A source file that contains generated code. */
abstract class GeneratedCodeFile extends RubyFile { }
/** A file contining comments suggesting it contains generated code. */
/** A file continuing comments suggesting it contains generated code. */
class GeneratedCommentFile extends GeneratedCodeFile {
GeneratedCommentFile() { this = any(GeneratedCodeComment c).getLocation().getFile() }
}

View File

@@ -0,0 +1,30 @@
/**
* Modeling for `ActiveJob`, a framweork for declaring and enqueueing jobs that
* ships with Rails.
* https://rubygems.org/gems/activejob
*/
private import codeql.ruby.ApiGraphs
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
/** Modeling for `ActiveJob`. */
module ActiveJob {
/**
* `ActiveJob::Serializers`
*/
module Serializers {
/**
* A call to `ActiveJob::Serializers.deserialize`, which interprets part of
* its argument as a Ruby constant.
*/
class DeserializeCall extends DataFlow::CallNode, CodeExecution::Range {
DeserializeCall() {
this =
API::getTopLevelMember("ActiveJob").getMember("Serializers").getAMethodCall("deserialize")
}
override DataFlow::Node getCode() { result = this.getArgument(0) }
}
}
}

View File

@@ -120,7 +120,7 @@ class GraphqlSchemaObjectClass extends ClassDeclaration {
* `GraphQL::Schema::RelayClassicMutation` or
* `GraphQL::Schema::Resolver`.
*
* Both of these classes have an overrideable `resolve` instance
* Both of these classes have an overridable `resolve` instance
* method which can receive user input in order to resolve a query or mutation.
*/
private class GraphqlResolvableClass extends ClassDeclaration {
@@ -144,7 +144,7 @@ private class GraphqlResolvableClass extends ClassDeclaration {
*
* ```rb
* module Mutation
* class NameAnInstrument < BaseMutationn
* class NameAnInstrument < BaseMutation
* argument :instrument_uuid, Types::Uuid,
* required: true,
* loads: ::Instrument,
@@ -188,7 +188,7 @@ class GraphqlResolveMethod extends Method, Http::Server::RequestHandler::Range {
*
* ```rb
* module Mutation
* class NameAnInstrument < BaseMutationn
* class NameAnInstrument < BaseMutation
* argument :instrument_uuid, Types::Uuid,
* required: true,
* loads: ::Instrument,

View File

@@ -95,7 +95,7 @@ module IO {
* popen([env,] cmd, mode="r" [, opt]) -> io
* popen([env,] cmd, mode="r" [, opt]) {|io| block } -> obj
* ```
* `IO.popen` does different things based on the the value of `cmd`:
* `IO.popen` does different things based on the value of `cmd`:
* ```
* "-" : fork
* commandline : command line string which is passed to a shell

View File

@@ -25,7 +25,7 @@ class HttpClientRequest extends Http::Client::Request::Range, DataFlow::CallNode
[
// One-off requests
API::getTopLevelMember("HTTPClient"),
// Conncection re-use
// Connection re-use
API::getTopLevelMember("HTTPClient").getInstance()
] and
requestNode = connectionNode.getReturn(method) and

View File

@@ -241,7 +241,7 @@ abstract class RegExp extends Ast::StringlikeLiteral {
/**
* Helper predicate for `escapingChar`.
* In order to avoid negative recusrion, we return a boolean.
* In order to avoid negative recursion, we return a boolean.
* This way, we can refer to `escaping(pos - 1).booleanNot()`
* rather than to a negated version of `escaping(pos)`.
*/

View File

@@ -1,5 +1,5 @@
/**
* Provides precicates for reasoning about bad tag filter vulnerabilities.
* Provides predicates for reasoning about bad tag filter vulnerabilities.
*/
import regexp.RegexpMatching
@@ -65,7 +65,7 @@ predicate isBadRegexpFilter(HtmlMatchingRegExp regexp, string msg) {
regexp.matches("<!-- foo --!>") and
exists(int a, int b | a != b |
regexp.fillsCaptureGroup("<!-- foo -->", a) and
// <!-- foo --> might be ambigously parsed (matching both capture groups), and that is ok here.
// <!-- foo --> might be ambiguously parsed (matching both capture groups), and that is ok here.
regexp.fillsCaptureGroup("<!-- foo --!>", b) and
not regexp.fillsCaptureGroup("<!-- foo --!>", a) and
msg =

View File

@@ -0,0 +1,36 @@
/**
* Provides utility classes and predicates for reasoning about `Kernel.open` and related methods.
*/
private import codeql.ruby.AST
private import codeql.ruby.DataFlow
private import codeql.ruby.AST
private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.core.Kernel::Kernel
/** A call to a method that might access a file or start a process. */
class AmbiguousPathCall extends DataFlow::CallNode {
string name;
AmbiguousPathCall() {
this.(KernelMethodCall).getMethodName() = "open" and
name = "Kernel.open"
or
this = API::getTopLevelMember("IO").getAMethodCall("read") and
not this = API::getTopLevelMember("File").getAMethodCall("read") and // needed in e.g. opal/opal, where some calls have both paths, but I'm not sure why
name = "IO.read"
}
/** Gets the name for the method being called. */
string getName() { result = name }
/** Gets the name for a safer method that can be used instead. */
string getReplacement() {
result = "File.read" and name = "IO.read"
or
result = "File.open" and name = "Kernel.open"
}
/** Gets the argument that specifies the path to be accessed. */
DataFlow::Node getPathArgument() { result = this.getArgument(0) }
}

View File

@@ -8,6 +8,8 @@ private import codeql.ruby.ApiGraphs
private import codeql.ruby.CFG
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.frameworks.ActiveJob
private import codeql.ruby.frameworks.core.Module
module UnsafeDeserialization {
/**
@@ -199,4 +201,27 @@ module UnsafeDeserialization {
toNode = callNode
)
}
/**
* A argument in a call to `Module.const_get`, considered as a sink for unsafe
* deserialization.
*
* Calls to `Module.const_get` can return arbitrary classes which can then be
* instantiated.
*/
class ConstGetCallArgument extends Sink {
ConstGetCallArgument() { this = any(Module::ModuleConstGetCallCodeExecution c).getCode() }
}
/**
* A argument in a call to `ActiveJob::Serializers.deserialize`, considered as
* a sink for unsafe deserialization.
*
* This is roughly equivalent to a call to `Module.const_get`.
*/
class ActiveJobSerializersDeserializeArgument extends Sink {
ActiveJobSerializersDeserializeArgument() {
this = any(ActiveJob::Serializers::DeserializeCall c).getCode()
}
}
}

View File

@@ -202,7 +202,7 @@ private predicate isFork(State q, InputSymbol s1, InputSymbol s2, State r1, Stat
//
// We additionally require that the there exists another InfiniteRepetitionQuantifier `mid` on the path from `q` to itself.
// This is done to avoid flagging regular expressions such as `/(a?)*b/` - that only has polynomial runtime, and is detected by `js/polynomial-redos`.
// The below code is therefore a heuritic, that only flags regular expressions such as `/(a*)*b/`,
// The below code is therefore a heuristic, that only flags regular expressions such as `/(a*)*b/`,
// and does not flag regular expressions such as `/(a?b?)c/`, but the latter pattern is not used frequently.
r1 = r2 and
q1 = q2 and

View File

@@ -1,5 +1,5 @@
/**
* Provides precicates for reasoning about which strings are matched by a regular expression,
* Provides predicates for reasoning about which strings are matched by a regular expression,
* and for testing which capture groups are filled when a particular regexp matches a string.
*/

View File

@@ -76,7 +76,7 @@ class StateTuple extends TStateTuple {
StateTuple() { this = MkStateTuple(q1, q2, q3) }
/**
* Gest a string repesentation of this tuple.
* Gest a string representation of this tuple.
*/
string toString() { result = "(" + q1 + ", " + q2 + ", " + q3 + ")" }

View File

@@ -23,7 +23,7 @@
### New Queries
* Added a new query, `rb/log-inection`, to detect cases where a malicious user may be able to forge log entries.
* Added a new query, `rb/log-injection`, to detect cases where a malicious user may be able to forge log entries.
* Added a new query, `rb/incomplete-multi-character-sanitization`. The query
finds string transformations that do not replace all occurrences of a
multi-character substring.

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* Added a new query, `rb/non-constant-kernel-open`, to detect uses of Kernel.open and related methods with non-constant values.

View File

@@ -2,7 +2,7 @@
### New Queries
* Added a new query, `rb/log-inection`, to detect cases where a malicious user may be able to forge log entries.
* Added a new query, `rb/log-injection`, to detect cases where a malicious user may be able to forge log entries.
* Added a new query, `rb/incomplete-multi-character-sanitization`. The query
finds string transformations that do not replace all occurrences of a
multi-character substring.

View File

@@ -1,6 +1,6 @@
/**
* @name Manually checking http verb instead of using built in rails routes and protections
* @description Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods.
* @description Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods.
* @kind path-problem
* @problem.severity error
* @security-severity 5.0
@@ -93,4 +93,4 @@ class HttpVerbConfig extends TaintTracking::Configuration {
from HttpVerbConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
"Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods."
"Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods."

View File

@@ -0,0 +1,46 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>If <code>Kernel.open</code> is given a file name that starts with a <code>|</code>
character, it will execute the remaining string as a shell command. If a
malicious user can control the file name, they can execute arbitrary code.
The same vulnerability applies to <code>IO.read</code>.
</p>
</overview>
<recommendation>
<p>Use <code>File.open</code> instead of <code>Kernel.open</code>, as the former
does not have this vulnerability. Similarly, use <code>File.read</code> instead
of <code>IO.read</code>.</p>
</recommendation>
<example>
<p>
The following example shows code that calls <code>Kernel.open</code> on a
user-supplied file path.
</p>
<sample src="examples/kernel_open.rb" />
<p>Instead, <code>File.open</code> should be used, as in the following example.</p>
<sample src="examples/file_open.rb" />
</example>
<references>
<li>
OWASP:
<a href="https://www.owasp.org/index.php/Command_Injection">Command Injection</a>.
</li>
<li>
Example CVE: <a href="https://www.ruby-lang.org/en/news/2021/05/02/os-command-injection-in-rdoc/">Command Injection in RDoc</a>.
</li>
</references>
</qhelp>

View File

@@ -1,46 +1,4 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>If <code>Kernel.open</code> is given a file name that starts with a <code>|</code>
character, it will execute the remaining string as a shell command. If a
malicious user can control the file name, they can execute arbitrary code.
The same vulnerability applies to <code>IO.read</code>.
</p>
</overview>
<recommendation>
<p>Use <code>File.open</code> instead of <code>Kernel.open</code>, as the former
does not have this vulnerability. Similarly, use <code>File.read</code> instead
of <code>IO.read</code>.</p>
</recommendation>
<example>
<p>
The following example shows code that calls <code>Kernel.open</code> on a
user-supplied file path.
</p>
<sample src="examples/kernel_open.rb" />
<p>Instead, <code>File.open</code> should be used, as in the following example.</p>
<sample src="examples/file_open.rb" />
</example>
<references>
<li>
OWASP:
<a href="https://www.owasp.org/index.php/Command_Injection">Command Injection</a>.
</li>
<li>
Example CVE: <a href="https://www.ruby-lang.org/en/news/2021/05/02/os-command-injection-in-rdoc/">Command Injection in RDoc</a>.
</li>
</references>
</qhelp>
<include src="KernelOpen.inc.qhelp" />
</qhelp>

View File

@@ -1,5 +1,5 @@
/**
* @name Use of `Kernel.open` or `IO.read`
* @name Use of `Kernel.open` or `IO.read` with user-controlled input
* @description Using `Kernel.open` or `IO.read` may allow a malicious
* user to execute arbitrary system commands.
* @kind path-problem
@@ -14,39 +14,12 @@
* external/cwe/cwe-073
*/
import codeql.ruby.AST
import codeql.ruby.ApiGraphs
import codeql.ruby.frameworks.core.Kernel::Kernel
import codeql.ruby.TaintTracking
import codeql.ruby.dataflow.BarrierGuards
import codeql.ruby.dataflow.RemoteFlowSources
import codeql.ruby.DataFlow
import codeql.ruby.TaintTracking
import codeql.ruby.dataflow.RemoteFlowSources
import codeql.ruby.dataflow.BarrierGuards
import DataFlow::PathGraph
/**
* A method call that has a suggested replacement.
*/
abstract class Replacement extends DataFlow::CallNode {
abstract string getFrom();
abstract string getTo();
}
class KernelOpenCall extends KernelMethodCall, Replacement {
KernelOpenCall() { this.getMethodName() = "open" }
override string getFrom() { result = "Kernel.open" }
override string getTo() { result = "File.open" }
}
class IOReadCall extends DataFlow::CallNode, Replacement {
IOReadCall() { this = API::getTopLevelMember("IO").getAMethodCall("read") }
override string getFrom() { result = "IO.read" }
override string getTo() { result = "File.read" }
}
import codeql.ruby.security.KernelOpenQuery
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "KernelOpen" }
@@ -54,9 +27,7 @@ class Configuration extends TaintTracking::Configuration {
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
exists(KernelOpenCall c | c.getArgument(0) = sink)
or
exists(IOReadCall c | c.getArgument(0) = sink)
sink = any(AmbiguousPathCall r).getPathArgument()
}
override predicate isSanitizer(DataFlow::Node node) {
@@ -73,5 +44,6 @@ where
sourceNode = source.getNode() and
call.getArgument(0) = sink.getNode()
select sink.getNode(), source, sink,
"This call to " + call.(Replacement).getFrom() + " depends on a $@. Replace it with " +
call.(Replacement).getTo() + ".", source.getNode(), "user-provided value"
"This call to " + call.(AmbiguousPathCall).getName() +
" depends on a $@. Consider replacing it with " + call.(AmbiguousPathCall).getReplacement() +
".", source.getNode(), "user-provided value"

View File

@@ -0,0 +1,4 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<include src="KernelOpen.inc.qhelp" />
</qhelp>

View File

@@ -0,0 +1,29 @@
/**
* @name Use of `Kernel.open` or `IO.read` with a non-constant value
* @description Using `Kernel.open` or `IO.read` may allow a malicious
* user to execute arbitrary system commands.
* @kind problem
* @problem.severity warning
* @security-severity 6.5
* @precision high
* @id rb/non-constant-kernel-open
* @tags correctness
* security
* external/cwe/cwe-078
* external/cwe/cwe-088
* external/cwe/cwe-073
*/
import codeql.ruby.security.KernelOpenQuery
import codeql.ruby.ast.Literal
from AmbiguousPathCall call
where
// there is not a constant string argument
not exists(call.getPathArgument().asExpr().getExpr().getConstantValue()) and
// if it's a format string, then the first argument is not a constant string
not call.getPathArgument().getALocalSource().asExpr().getExpr().(StringLiteral).getComponent(0)
instanceof StringTextComponent
select call,
"Call to " + call.getName() + " with a non-constant value. Consider replacing it with " +
call.getReplacement() + "."

View File

@@ -23,10 +23,10 @@ nodes
| ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | semmle.label | ...[...] |
subpaths
#select
| ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env : | ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method : | ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method : | ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method : | ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol : | ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env : | ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env : | ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method : | ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method : | ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method : | ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol : | ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods. |
| ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env : | ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mapping resources and verbs to specific methods. |

View File

@@ -9,5 +9,5 @@ nodes
| KernelOpen.rb:5:13:5:16 | file | semmle.label | file |
subpaths
#select
| KernelOpen.rb:4:10:4:13 | file | KernelOpen.rb:3:12:3:17 | call to params : | KernelOpen.rb:4:10:4:13 | file | This call to Kernel.open depends on a $@. Replace it with File.open. | KernelOpen.rb:3:12:3:17 | call to params | user-provided value |
| KernelOpen.rb:5:13:5:16 | file | KernelOpen.rb:3:12:3:17 | call to params : | KernelOpen.rb:5:13:5:16 | file | This call to IO.read depends on a $@. Replace it with File.read. | KernelOpen.rb:3:12:3:17 | call to params | user-provided value |
| KernelOpen.rb:4:10:4:13 | file | KernelOpen.rb:3:12:3:17 | call to params : | KernelOpen.rb:4:10:4:13 | file | This call to Kernel.open depends on a $@. Consider replacing it with File.open. | KernelOpen.rb:3:12:3:17 | call to params | user-provided value |
| KernelOpen.rb:5:13:5:16 | file | KernelOpen.rb:3:12:3:17 | call to params : | KernelOpen.rb:5:13:5:16 | file | This call to IO.read depends on a $@. Consider replacing it with File.read. | KernelOpen.rb:3:12:3:17 | call to params | user-provided value |

View File

@@ -0,0 +1,4 @@
| NonConstantKernelOpen.rb:4:5:4:14 | call to open | Call to Kernel.open with a non-constant value. Consider replacing it with File.open. |
| NonConstantKernelOpen.rb:5:5:5:17 | call to read | Call to IO.read with a non-constant value. Consider replacing it with File.read. |
| NonConstantKernelOpen.rb:9:5:9:21 | call to open | Call to Kernel.open with a non-constant value. Consider replacing it with File.open. |
| NonConstantKernelOpen.rb:19:5:19:33 | call to open | Call to Kernel.open with a non-constant value. Consider replacing it with File.open. |

View File

@@ -0,0 +1 @@
queries/security/cwe-078/NonConstantKernelOpen.ql

View File

@@ -0,0 +1,23 @@
class UsersController < ActionController::Base
def create
file = params[:file]
open(file) # BAD
IO.read(file) # BAD
File.open(file).read # GOOD
Kernel.open(file) # BAD
File.open(file, "r") # GOOD
Kernel.open("constant") # GOOD
IO.read("constant") # GOOD
Kernel.open("this is #{fine}") # GOOD
Kernel.open("#{this_is} bad") # BAD
open("| #{this_is_an_explicit_command} foo bar") # GOOD
end
end

View File

@@ -1,29 +1,32 @@
edges
| CodeInjection.rb:3:12:3:17 | call to params : | CodeInjection.rb:3:12:3:24 | ...[...] : |
| CodeInjection.rb:3:12:3:24 | ...[...] : | CodeInjection.rb:6:10:6:13 | code |
| CodeInjection.rb:3:12:3:24 | ...[...] : | CodeInjection.rb:18:20:18:23 | code |
| CodeInjection.rb:3:12:3:24 | ...[...] : | CodeInjection.rb:21:21:21:24 | code |
| CodeInjection.rb:3:12:3:24 | ...[...] : | CodeInjection.rb:27:15:27:18 | code |
| CodeInjection.rb:3:12:3:24 | ...[...] : | CodeInjection.rb:30:19:30:22 | code |
| CodeInjection.rb:3:12:3:24 | ...[...] : | CodeInjection.rb:36:24:36:27 | code : |
| CodeInjection.rb:36:24:36:27 | code : | CodeInjection.rb:36:10:36:28 | call to escape |
| CodeInjection.rb:5:12:5:17 | call to params : | CodeInjection.rb:5:12:5:24 | ...[...] : |
| CodeInjection.rb:5:12:5:24 | ...[...] : | CodeInjection.rb:8:10:8:13 | code |
| CodeInjection.rb:5:12:5:24 | ...[...] : | CodeInjection.rb:20:20:20:23 | code |
| CodeInjection.rb:5:12:5:24 | ...[...] : | CodeInjection.rb:23:21:23:24 | code |
| CodeInjection.rb:5:12:5:24 | ...[...] : | CodeInjection.rb:29:15:29:18 | code |
| CodeInjection.rb:5:12:5:24 | ...[...] : | CodeInjection.rb:32:19:32:22 | code |
| CodeInjection.rb:5:12:5:24 | ...[...] : | CodeInjection.rb:38:24:38:27 | code : |
| CodeInjection.rb:5:12:5:24 | ...[...] : | CodeInjection.rb:41:40:41:43 | code |
| CodeInjection.rb:38:24:38:27 | code : | CodeInjection.rb:38:10:38:28 | call to escape |
nodes
| CodeInjection.rb:3:12:3:17 | call to params : | semmle.label | call to params : |
| CodeInjection.rb:3:12:3:24 | ...[...] : | semmle.label | ...[...] : |
| CodeInjection.rb:6:10:6:13 | code | semmle.label | code |
| CodeInjection.rb:9:10:9:15 | call to params | semmle.label | call to params |
| CodeInjection.rb:18:20:18:23 | code | semmle.label | code |
| CodeInjection.rb:21:21:21:24 | code | semmle.label | code |
| CodeInjection.rb:27:15:27:18 | code | semmle.label | code |
| CodeInjection.rb:30:19:30:22 | code | semmle.label | code |
| CodeInjection.rb:36:10:36:28 | call to escape | semmle.label | call to escape |
| CodeInjection.rb:36:24:36:27 | code : | semmle.label | code : |
| CodeInjection.rb:5:12:5:17 | call to params : | semmle.label | call to params : |
| CodeInjection.rb:5:12:5:24 | ...[...] : | semmle.label | ...[...] : |
| CodeInjection.rb:8:10:8:13 | code | semmle.label | code |
| CodeInjection.rb:11:10:11:15 | call to params | semmle.label | call to params |
| CodeInjection.rb:20:20:20:23 | code | semmle.label | code |
| CodeInjection.rb:23:21:23:24 | code | semmle.label | code |
| CodeInjection.rb:29:15:29:18 | code | semmle.label | code |
| CodeInjection.rb:32:19:32:22 | code | semmle.label | code |
| CodeInjection.rb:38:10:38:28 | call to escape | semmle.label | call to escape |
| CodeInjection.rb:38:24:38:27 | code : | semmle.label | code : |
| CodeInjection.rb:41:40:41:43 | code | semmle.label | code |
subpaths
#select
| CodeInjection.rb:6:10:6:13 | code | CodeInjection.rb:3:12:3:17 | call to params : | CodeInjection.rb:6:10:6:13 | code | This code execution depends on a $@. | CodeInjection.rb:3:12:3:17 | call to params | user-provided value |
| CodeInjection.rb:9:10:9:15 | call to params | CodeInjection.rb:9:10:9:15 | call to params | CodeInjection.rb:9:10:9:15 | call to params | This code execution depends on a $@. | CodeInjection.rb:9:10:9:15 | call to params | user-provided value |
| CodeInjection.rb:18:20:18:23 | code | CodeInjection.rb:3:12:3:17 | call to params : | CodeInjection.rb:18:20:18:23 | code | This code execution depends on a $@. | CodeInjection.rb:3:12:3:17 | call to params | user-provided value |
| CodeInjection.rb:21:21:21:24 | code | CodeInjection.rb:3:12:3:17 | call to params : | CodeInjection.rb:21:21:21:24 | code | This code execution depends on a $@. | CodeInjection.rb:3:12:3:17 | call to params | user-provided value |
| CodeInjection.rb:27:15:27:18 | code | CodeInjection.rb:3:12:3:17 | call to params : | CodeInjection.rb:27:15:27:18 | code | This code execution depends on a $@. | CodeInjection.rb:3:12:3:17 | call to params | user-provided value |
| CodeInjection.rb:30:19:30:22 | code | CodeInjection.rb:3:12:3:17 | call to params : | CodeInjection.rb:30:19:30:22 | code | This code execution depends on a $@. | CodeInjection.rb:3:12:3:17 | call to params | user-provided value |
| CodeInjection.rb:36:10:36:28 | call to escape | CodeInjection.rb:3:12:3:17 | call to params : | CodeInjection.rb:36:10:36:28 | call to escape | This code execution depends on a $@. | CodeInjection.rb:3:12:3:17 | call to params | user-provided value |
| CodeInjection.rb:8:10:8:13 | code | CodeInjection.rb:5:12:5:17 | call to params : | CodeInjection.rb:8:10:8:13 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:11:10:11:15 | call to params | CodeInjection.rb:11:10:11:15 | call to params | CodeInjection.rb:11:10:11:15 | call to params | This code execution depends on a $@. | CodeInjection.rb:11:10:11:15 | call to params | user-provided value |
| CodeInjection.rb:20:20:20:23 | code | CodeInjection.rb:5:12:5:17 | call to params : | CodeInjection.rb:20:20:20:23 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:23:21:23:24 | code | CodeInjection.rb:5:12:5:17 | call to params : | CodeInjection.rb:23:21:23:24 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:29:15:29:18 | code | CodeInjection.rb:5:12:5:17 | call to params : | CodeInjection.rb:29:15:29:18 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:32:19:32:22 | code | CodeInjection.rb:5:12:5:17 | call to params : | CodeInjection.rb:32:19:32:22 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:38:10:38:28 | call to escape | CodeInjection.rb:5:12:5:17 | call to params : | CodeInjection.rb:38:10:38:28 | call to escape | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |
| CodeInjection.rb:41:40:41:43 | code | CodeInjection.rb:5:12:5:17 | call to params : | CodeInjection.rb:41:40:41:43 | code | This code execution depends on a $@. | CodeInjection.rb:5:12:5:17 | call to params | user-provided value |

View File

@@ -1,3 +1,5 @@
require 'active_job'
class UsersController < ActionController::Base
def create
code = params[:code]
@@ -22,18 +24,21 @@ class UsersController < ActionController::Base
# GOOD
Bar.class_eval(code)
# BAD
const_get(code)
# BAD
Foo.const_get(code)
# GOOD
Bar.const_get(code)
# BAD
eval(Regexp.escape(code))
# BAD
ActiveJob::Serializers.deserialize(code)
end
def update
@@ -62,8 +67,8 @@ class Bar
def self.class_eval(x)
true
end
def self.const_get(x)
true
end
end
end

View File

@@ -1,61 +1,69 @@
edges
| UnsafeDeserialization.rb:9:39:9:44 | call to params : | UnsafeDeserialization.rb:9:39:9:50 | ...[...] : |
| UnsafeDeserialization.rb:9:39:9:50 | ...[...] : | UnsafeDeserialization.rb:10:27:10:41 | serialized_data |
| UnsafeDeserialization.rb:15:39:15:44 | call to params : | UnsafeDeserialization.rb:15:39:15:50 | ...[...] : |
| UnsafeDeserialization.rb:15:39:15:50 | ...[...] : | UnsafeDeserialization.rb:16:30:16:44 | serialized_data |
| UnsafeDeserialization.rb:21:17:21:22 | call to params : | UnsafeDeserialization.rb:21:17:21:28 | ...[...] : |
| UnsafeDeserialization.rb:21:17:21:28 | ...[...] : | UnsafeDeserialization.rb:22:24:22:32 | json_data |
| UnsafeDeserialization.rb:27:17:27:22 | call to params : | UnsafeDeserialization.rb:27:17:27:28 | ...[...] : |
| UnsafeDeserialization.rb:27:17:27:28 | ...[...] : | UnsafeDeserialization.rb:28:27:28:35 | json_data |
| UnsafeDeserialization.rb:39:17:39:22 | call to params : | UnsafeDeserialization.rb:39:17:39:28 | ...[...] : |
| UnsafeDeserialization.rb:39:17:39:28 | ...[...] : | UnsafeDeserialization.rb:40:24:40:32 | yaml_data |
| UnsafeDeserialization.rb:51:17:51:22 | call to params : | UnsafeDeserialization.rb:51:17:51:28 | ...[...] : |
| UnsafeDeserialization.rb:51:17:51:28 | ...[...] : | UnsafeDeserialization.rb:52:22:52:30 | json_data |
| UnsafeDeserialization.rb:51:17:51:28 | ...[...] : | UnsafeDeserialization.rb:53:22:53:30 | json_data |
| UnsafeDeserialization.rb:58:17:58:22 | call to params : | UnsafeDeserialization.rb:58:17:58:28 | ...[...] : |
| UnsafeDeserialization.rb:58:17:58:28 | ...[...] : | UnsafeDeserialization.rb:68:23:68:31 | json_data |
| UnsafeDeserialization.rb:80:11:80:16 | call to params : | UnsafeDeserialization.rb:80:11:80:22 | ...[...] : |
| UnsafeDeserialization.rb:80:11:80:22 | ...[...] : | UnsafeDeserialization.rb:81:34:81:36 | xml |
| UnsafeDeserialization.rb:86:17:86:22 | call to params : | UnsafeDeserialization.rb:86:17:86:28 | ...[...] : |
| UnsafeDeserialization.rb:86:17:86:28 | ...[...] : | UnsafeDeserialization.rb:87:25:87:33 | yaml_data |
| UnsafeDeserialization.rb:10:39:10:44 | call to params : | UnsafeDeserialization.rb:10:39:10:50 | ...[...] : |
| UnsafeDeserialization.rb:10:39:10:50 | ...[...] : | UnsafeDeserialization.rb:11:27:11:41 | serialized_data |
| UnsafeDeserialization.rb:16:39:16:44 | call to params : | UnsafeDeserialization.rb:16:39:16:50 | ...[...] : |
| UnsafeDeserialization.rb:16:39:16:50 | ...[...] : | UnsafeDeserialization.rb:17:30:17:44 | serialized_data |
| UnsafeDeserialization.rb:22:17:22:22 | call to params : | UnsafeDeserialization.rb:22:17:22:28 | ...[...] : |
| UnsafeDeserialization.rb:22:17:22:28 | ...[...] : | UnsafeDeserialization.rb:23:24:23:32 | json_data |
| UnsafeDeserialization.rb:28:17:28:22 | call to params : | UnsafeDeserialization.rb:28:17:28:28 | ...[...] : |
| UnsafeDeserialization.rb:28:17:28:28 | ...[...] : | UnsafeDeserialization.rb:29:27:29:35 | json_data |
| UnsafeDeserialization.rb:40:17:40:22 | call to params : | UnsafeDeserialization.rb:40:17:40:28 | ...[...] : |
| UnsafeDeserialization.rb:40:17:40:28 | ...[...] : | UnsafeDeserialization.rb:41:24:41:32 | yaml_data |
| UnsafeDeserialization.rb:52:17:52:22 | call to params : | UnsafeDeserialization.rb:52:17:52:28 | ...[...] : |
| UnsafeDeserialization.rb:52:17:52:28 | ...[...] : | UnsafeDeserialization.rb:53:22:53:30 | json_data |
| UnsafeDeserialization.rb:52:17:52:28 | ...[...] : | UnsafeDeserialization.rb:54:22:54:30 | json_data |
| UnsafeDeserialization.rb:59:17:59:22 | call to params : | UnsafeDeserialization.rb:59:17:59:28 | ...[...] : |
| UnsafeDeserialization.rb:59:17:59:28 | ...[...] : | UnsafeDeserialization.rb:69:23:69:31 | json_data |
| UnsafeDeserialization.rb:81:11:81:16 | call to params : | UnsafeDeserialization.rb:81:11:81:22 | ...[...] : |
| UnsafeDeserialization.rb:81:11:81:22 | ...[...] : | UnsafeDeserialization.rb:82:34:82:36 | xml |
| UnsafeDeserialization.rb:87:17:87:22 | call to params : | UnsafeDeserialization.rb:87:17:87:28 | ...[...] : |
| UnsafeDeserialization.rb:87:17:87:28 | ...[...] : | UnsafeDeserialization.rb:88:25:88:33 | yaml_data |
| UnsafeDeserialization.rb:93:30:93:35 | call to params : | UnsafeDeserialization.rb:93:30:93:43 | ...[...] |
| UnsafeDeserialization.rb:99:48:99:53 | call to params : | UnsafeDeserialization.rb:99:48:99:61 | ...[...] |
nodes
| UnsafeDeserialization.rb:9:39:9:44 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:9:39:9:50 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:10:27:10:41 | serialized_data | semmle.label | serialized_data |
| UnsafeDeserialization.rb:15:39:15:44 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:15:39:15:50 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:16:30:16:44 | serialized_data | semmle.label | serialized_data |
| UnsafeDeserialization.rb:21:17:21:22 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:21:17:21:28 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:22:24:22:32 | json_data | semmle.label | json_data |
| UnsafeDeserialization.rb:27:17:27:22 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:27:17:27:28 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:28:27:28:35 | json_data | semmle.label | json_data |
| UnsafeDeserialization.rb:39:17:39:22 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:39:17:39:28 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:40:24:40:32 | yaml_data | semmle.label | yaml_data |
| UnsafeDeserialization.rb:51:17:51:22 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:51:17:51:28 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:52:22:52:30 | json_data | semmle.label | json_data |
| UnsafeDeserialization.rb:10:39:10:44 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:10:39:10:50 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:11:27:11:41 | serialized_data | semmle.label | serialized_data |
| UnsafeDeserialization.rb:16:39:16:44 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:16:39:16:50 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:17:30:17:44 | serialized_data | semmle.label | serialized_data |
| UnsafeDeserialization.rb:22:17:22:22 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:22:17:22:28 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:23:24:23:32 | json_data | semmle.label | json_data |
| UnsafeDeserialization.rb:28:17:28:22 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:28:17:28:28 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:29:27:29:35 | json_data | semmle.label | json_data |
| UnsafeDeserialization.rb:40:17:40:22 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:40:17:40:28 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:41:24:41:32 | yaml_data | semmle.label | yaml_data |
| UnsafeDeserialization.rb:52:17:52:22 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:52:17:52:28 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:53:22:53:30 | json_data | semmle.label | json_data |
| UnsafeDeserialization.rb:58:17:58:22 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:58:17:58:28 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:68:23:68:31 | json_data | semmle.label | json_data |
| UnsafeDeserialization.rb:80:11:80:16 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:80:11:80:22 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:81:34:81:36 | xml | semmle.label | xml |
| UnsafeDeserialization.rb:86:17:86:22 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:86:17:86:28 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:87:25:87:33 | yaml_data | semmle.label | yaml_data |
| UnsafeDeserialization.rb:54:22:54:30 | json_data | semmle.label | json_data |
| UnsafeDeserialization.rb:59:17:59:22 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:59:17:59:28 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:69:23:69:31 | json_data | semmle.label | json_data |
| UnsafeDeserialization.rb:81:11:81:16 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:81:11:81:22 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:82:34:82:36 | xml | semmle.label | xml |
| UnsafeDeserialization.rb:87:17:87:22 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:87:17:87:28 | ...[...] : | semmle.label | ...[...] : |
| UnsafeDeserialization.rb:88:25:88:33 | yaml_data | semmle.label | yaml_data |
| UnsafeDeserialization.rb:93:30:93:35 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:93:30:93:43 | ...[...] | semmle.label | ...[...] |
| UnsafeDeserialization.rb:99:48:99:53 | call to params : | semmle.label | call to params : |
| UnsafeDeserialization.rb:99:48:99:61 | ...[...] | semmle.label | ...[...] |
subpaths
#select
| UnsafeDeserialization.rb:10:27:10:41 | serialized_data | UnsafeDeserialization.rb:9:39:9:44 | call to params : | UnsafeDeserialization.rb:10:27:10:41 | serialized_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:9:39:9:44 | call to params | user-provided value |
| UnsafeDeserialization.rb:16:30:16:44 | serialized_data | UnsafeDeserialization.rb:15:39:15:44 | call to params : | UnsafeDeserialization.rb:16:30:16:44 | serialized_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:15:39:15:44 | call to params | user-provided value |
| UnsafeDeserialization.rb:22:24:22:32 | json_data | UnsafeDeserialization.rb:21:17:21:22 | call to params : | UnsafeDeserialization.rb:22:24:22:32 | json_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:21:17:21:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:28:27:28:35 | json_data | UnsafeDeserialization.rb:27:17:27:22 | call to params : | UnsafeDeserialization.rb:28:27:28:35 | json_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:27:17:27:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:40:24:40:32 | yaml_data | UnsafeDeserialization.rb:39:17:39:22 | call to params : | UnsafeDeserialization.rb:40:24:40:32 | yaml_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:39:17:39:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:52:22:52:30 | json_data | UnsafeDeserialization.rb:51:17:51:22 | call to params : | UnsafeDeserialization.rb:52:22:52:30 | json_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:51:17:51:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:53:22:53:30 | json_data | UnsafeDeserialization.rb:51:17:51:22 | call to params : | UnsafeDeserialization.rb:53:22:53:30 | json_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:51:17:51:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:68:23:68:31 | json_data | UnsafeDeserialization.rb:58:17:58:22 | call to params : | UnsafeDeserialization.rb:68:23:68:31 | json_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:58:17:58:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:81:34:81:36 | xml | UnsafeDeserialization.rb:80:11:80:16 | call to params : | UnsafeDeserialization.rb:81:34:81:36 | xml | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:80:11:80:16 | call to params | user-provided value |
| UnsafeDeserialization.rb:87:25:87:33 | yaml_data | UnsafeDeserialization.rb:86:17:86:22 | call to params : | UnsafeDeserialization.rb:87:25:87:33 | yaml_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:86:17:86:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:11:27:11:41 | serialized_data | UnsafeDeserialization.rb:10:39:10:44 | call to params : | UnsafeDeserialization.rb:11:27:11:41 | serialized_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:10:39:10:44 | call to params | user-provided value |
| UnsafeDeserialization.rb:17:30:17:44 | serialized_data | UnsafeDeserialization.rb:16:39:16:44 | call to params : | UnsafeDeserialization.rb:17:30:17:44 | serialized_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:16:39:16:44 | call to params | user-provided value |
| UnsafeDeserialization.rb:23:24:23:32 | json_data | UnsafeDeserialization.rb:22:17:22:22 | call to params : | UnsafeDeserialization.rb:23:24:23:32 | json_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:22:17:22:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:29:27:29:35 | json_data | UnsafeDeserialization.rb:28:17:28:22 | call to params : | UnsafeDeserialization.rb:29:27:29:35 | json_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:28:17:28:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:41:24:41:32 | yaml_data | UnsafeDeserialization.rb:40:17:40:22 | call to params : | UnsafeDeserialization.rb:41:24:41:32 | yaml_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:40:17:40:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:53:22:53:30 | json_data | UnsafeDeserialization.rb:52:17:52:22 | call to params : | UnsafeDeserialization.rb:53:22:53:30 | json_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:52:17:52:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:54:22:54:30 | json_data | UnsafeDeserialization.rb:52:17:52:22 | call to params : | UnsafeDeserialization.rb:54:22:54:30 | json_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:52:17:52:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:69:23:69:31 | json_data | UnsafeDeserialization.rb:59:17:59:22 | call to params : | UnsafeDeserialization.rb:69:23:69:31 | json_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:59:17:59:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:82:34:82:36 | xml | UnsafeDeserialization.rb:81:11:81:16 | call to params : | UnsafeDeserialization.rb:82:34:82:36 | xml | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:81:11:81:16 | call to params | user-provided value |
| UnsafeDeserialization.rb:88:25:88:33 | yaml_data | UnsafeDeserialization.rb:87:17:87:22 | call to params : | UnsafeDeserialization.rb:88:25:88:33 | yaml_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:87:17:87:22 | call to params | user-provided value |
| UnsafeDeserialization.rb:93:30:93:43 | ...[...] | UnsafeDeserialization.rb:93:30:93:35 | call to params : | UnsafeDeserialization.rb:93:30:93:43 | ...[...] | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:93:30:93:35 | call to params | user-provided value |
| UnsafeDeserialization.rb:99:48:99:61 | ...[...] | UnsafeDeserialization.rb:99:48:99:53 | call to params : | UnsafeDeserialization.rb:99:48:99:61 | ...[...] | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:99:48:99:53 | call to params | user-provided value |

View File

@@ -1,3 +1,4 @@
require "active_job"
require "base64"
require "json"
require "oj"
@@ -86,4 +87,16 @@ class UsersController < ActionController::Base
yaml_data = params[:key]
object = Psych.load yaml_data
end
# BAD - user input determines which class is instantiated
def route12
klass = Module.const_get(params[:class])
object = klass.new
end
# BAD - user input determines which class is instantiated
def route13
klass = ActiveJob::Serializers.deserialize(params[:class])
object = klass.new
end
end

View File

@@ -230,18 +230,18 @@ def _partition(l, pred):
return map(list, _partition_iter(l, pred))
def _is_in_qltest_collapsed_hierachy(cls: schema.Class, lookup: typing.Dict[str, schema.Class]):
return "qltest_collapse_hierarchy" in cls.pragmas or _is_under_qltest_collapsed_hierachy(cls, lookup)
def _is_in_qltest_collapsed_hierarchy(cls: schema.Class, lookup: typing.Dict[str, schema.Class]):
return "qltest_collapse_hierarchy" in cls.pragmas or _is_under_qltest_collapsed_hierarchy(cls, lookup)
def _is_under_qltest_collapsed_hierachy(cls: schema.Class, lookup: typing.Dict[str, schema.Class]):
def _is_under_qltest_collapsed_hierarchy(cls: schema.Class, lookup: typing.Dict[str, schema.Class]):
return "qltest_uncollapse_hierarchy" not in cls.pragmas and any(
_is_in_qltest_collapsed_hierachy(lookup[b], lookup) for b in cls.bases)
_is_in_qltest_collapsed_hierarchy(lookup[b], lookup) for b in cls.bases)
def _should_skip_qltest(cls: schema.Class, lookup: typing.Dict[str, schema.Class]):
return "qltest_skip" in cls.pragmas or not (
cls.final or "qltest_collapse_hierarchy" in cls.pragmas) or _is_under_qltest_collapsed_hierachy(
cls.final or "qltest_collapse_hierarchy" in cls.pragmas) or _is_under_qltest_collapsed_hierarchy(
cls, lookup)

View File

@@ -308,7 +308,7 @@ class SwiftDispatcher {
static FilePath getFilePath(llvm::StringRef path) {
// TODO: this needs more testing
// TODO: check canonicaliztion of names on a case insensitive filesystems
// TODO: check canonicalization of names on a case insensitive filesystems
// TODO: make symlink resolution conditional on CODEQL_PRESERVE_SYMLINKS=true
llvm::SmallString<PATH_MAX> realPath;
if (std::error_code ec = llvm::sys::fs::real_path(path, realPath)) {

View File

@@ -94,7 +94,7 @@ private predicate isBooleanConstant(ControlFlowElement n, boolean value) {
mustHaveBooleanCompletion(n) and
value = n.asAstNode().(BooleanLiteralExpr).getValue()
or
// Boolean consants hidden inside conversions are also
// Boolean constants hidden inside conversions are also
// constants that resolve to the same value.
exists(ControlFlowElement parent |
parent.asAstNode() = n.asAstNode().getResolveStep() and

View File

@@ -1074,7 +1074,7 @@ module Exprs {
/**
* The control-flow for assignments where the left-hand side has
* direct-to-implmentation-access semantics.
* direct-to-implementation-access semantics.
*/
class PropertyAssignExpr extends AssignExprTree {
AccessorDecl accessorDecl;

View File

@@ -885,7 +885,7 @@ module TestOutput {
/**
* Gets a string used to resolve ties in node and edge ordering.
*/
string getOrderDisambuigation() { result = "" }
string getOrderDisambiguation() { result = "" }
}
query predicate nodes(RelevantNode n, string attr, string val) {
@@ -900,7 +900,7 @@ module TestOutput {
order by
l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(),
l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString(),
p.getOrderDisambuigation()
p.getOrderDisambiguation()
)
).toString()
}
@@ -923,7 +923,7 @@ module TestOutput {
order by
l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(),
l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString(),
s.getOrderDisambuigation()
s.getOrderDisambiguation()
)
).toString()
}

View File

@@ -71,7 +71,7 @@ newtype TDataFlowCall =
TNormalCall(ApplyExprCfgNode call) or
TPropertyGetterCall(PropertyGetterCfgNode getter) or
TPropertySetterCall(PropertySetterCfgNode setter) or
TPropertyObserverCall(PropertyObserverCfgNode obserer) or
TPropertyObserverCall(PropertyObserverCfgNode observer) or
TSummaryCall(FlowSummaryImpl::Public::SummarizedCallable c, Node receiver) {
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
}
@@ -191,7 +191,7 @@ class PropertyObserverCall extends DataFlowCall, TPropertyObserverCall {
result = observer.getBase()
or
// TODO: This is correct for `willSet` (which takes a `newValue` parameter),
// but for `didSet` (which takes an `oldValue` paramter) we need an rvalue
// but for `didSet` (which takes an `oldValue` parameter) we need an rvalue
// for `getBase()`.
i = 0 and
result = observer.getSource()

View File

@@ -11,7 +11,7 @@ class MyRelevantNode extends RelevantNode {
private AstNode asAstNode() { result = this.getNode().asAstNode() }
override string getOrderDisambuigation() {
override string getOrderDisambiguation() {
result = this.asAstNode().getPrimaryQlClasses()
or
not exists(this.asAstNode()) and result = ""

View File

@@ -12,7 +12,7 @@ function RegisterExtractorPack(id)
return nil
end
-- removes upsupported CLI arg including the following how_many args
-- removes unsupported CLI arg including the following how_many args
function strip_unsupported_arg(args, arg, how_many)
local index = indexOf(args, arg)
if index then