move model of printf style calls to StringFormatters.qll

This commit is contained in:
erik-krogh
2022-10-17 12:28:29 +02:00
parent dbf2673a91
commit a2b924bbdf
3 changed files with 73 additions and 76 deletions

View File

@@ -23,3 +23,4 @@ private import codeql.ruby.frameworks.HttpClients
private import codeql.ruby.frameworks.XmlParsing
private import codeql.ruby.frameworks.ActionDispatch
private import codeql.ruby.frameworks.PosixSpawn
private import codeql.ruby.frameworks.StringFormatters

View File

@@ -0,0 +1,70 @@
/**
* Provides classes for modeling string formatting libraries.
*/
private import codeql.ruby.ast.Call
private import codeql.ruby.DataFlow
private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.core.IO
/**
* A call to `printf` or `sprintf`.
*/
abstract class PrintfStyleCall extends DataFlow::CallNode {
// We assume that most printf-like calls have the signature f(format_string, args...)
/**
* Gets the format string of this call.
*/
DataFlow::Node getFormatString() { result = this.getArgument(0) }
/**
* Gets then `n`th formatted argument of this call.
*/
DataFlow::Node getFormatArgument(int n) { n >= 0 and result = this.getArgument(n + 1) }
}
/**
* A call to `Kernel.printf`.
*/
class KernelPrintfCall extends PrintfStyleCall {
KernelPrintfCall() {
this = API::getTopLevelMember("Kernel").getAMethodCall("printf")
or
this.asExpr().getExpr() instanceof UnknownMethodCall and
this.getMethodName() = "printf"
}
// Kernel#printf supports two signatures:
// printf(io, string, ...)
// printf(string, ...)
override DataFlow::Node getFormatString() {
// Because `printf` has two different signatures, we can't be sure which
// argument is the format string, so we use a heuristic:
// If the first argument has a string value, then we assume it is the format string.
// Otherwise we treat both the first and second args as the format string.
if this.getArgument(0).getExprNode().getConstantValue().isString(_)
then result = this.getArgument(0)
else result = this.getArgument([0, 1])
}
}
/**
* A call to `Kernel.sprintf`.
*/
class KernelSprintfCall extends PrintfStyleCall {
KernelSprintfCall() {
this = API::getTopLevelMember("Kernel").getAMethodCall("sprintf")
or
this.asExpr().getExpr() instanceof UnknownMethodCall and
this.getMethodName() = "sprintf"
}
}
/**
* A call to `IO#printf`.
*/
class IOPrintfCall extends PrintfStyleCall {
IOPrintfCall() {
this.getReceiver() instanceof IO::IOInstance and this.getMethodName() = "printf"
}
}

View File

@@ -2,82 +2,8 @@
* Provides Ruby-specific imports and classes needed for `TaintedFormatStringQuery` and `TaintedFormatStringCustomizations`.
*/
import codeql.ruby.AST
import codeql.ruby.frameworks.StringFormatters
import codeql.ruby.DataFlow
import codeql.ruby.dataflow.RemoteFlowSources
import codeql.ruby.ApiGraphs
import codeql.ruby.TaintTracking
private import codeql.ruby.frameworks.Files
private import codeql.ruby.frameworks.core.IO
private import codeql.ruby.controlflow.CfgNodes
/**
* A call to `printf` or `sprintf`.
*/
abstract class PrintfStyleCall extends DataFlow::CallNode {
// We assume that most printf-like calls have the signature f(format_string, args...)
/**
* Gets the format string of this call.
*/
DataFlow::Node getFormatString() { result = this.getArgument(0) }
/**
* Gets then `n`th formatted argument of this call.
*/
DataFlow::Node getFormatArgument(int n) { n >= 0 and result = this.getArgument(n + 1) }
/** Holds if this call returns the formatted string. */
predicate returnsFormatted() { any() }
}
/**
* A call to `Kernel.printf`.
*/
class KernelPrintfCall extends PrintfStyleCall {
KernelPrintfCall() {
this = API::getTopLevelMember("Kernel").getAMethodCall("printf")
or
this.asExpr().getExpr() instanceof UnknownMethodCall and
this.getMethodName() = "printf"
}
// Kernel#printf supports two signatures:
// printf(io, string, ...)
// printf(string, ...)
override DataFlow::Node getFormatString() {
// Because `printf` has two different signatures, we can't be sure which
// argument is the format string, so we use a heuristic:
// If the first argument has a string value, then we assume it is the format string.
// Otherwise we treat both the first and second args as the format string.
if this.getArgument(0).getExprNode().getConstantValue().isString(_)
then result = this.getArgument(0)
else result = this.getArgument([0, 1])
}
override predicate returnsFormatted() { none() }
}
/**
* A call to `Kernel.sprintf`.
*/
class KernelSprintfCall extends PrintfStyleCall {
KernelSprintfCall() {
this = API::getTopLevelMember("Kernel").getAMethodCall("sprintf")
or
this.asExpr().getExpr() instanceof UnknownMethodCall and
this.getMethodName() = "sprintf"
}
override predicate returnsFormatted() { any() }
}
/**
* A call to `IO#printf`.
*/
class IOPrintfCall extends PrintfStyleCall {
IOPrintfCall() {
this.getReceiver() instanceof IO::IOInstance and this.getMethodName() = "printf"
}
override predicate returnsFormatted() { none() }
}
import codeql.ruby.DataFlow