Files
codeql/csharp/ql/src/API Abuse/FormatInvalid.ql

87 lines
3.0 KiB
Plaintext

/**
* @name Invalid string formatting
* @description Calling 'string.Format()' with either an invalid format string or incorrect
* number of arguments may result in dropped arguments or a 'System.FormatException'.
* @kind path-problem
* @problem.severity error
* @precision high
* @id cs/invalid-string-formatting
* @tags reliability
* maintainability
*/
import csharp
import semmle.code.csharp.frameworks.Format
import DataFlow::PathGraph
private class FormatConfiguration extends DataFlow::Configuration {
FormatConfiguration() { this = "format" }
override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLiteral }
override predicate isSink(DataFlow::Node n) {
exists(FormatCall c | n.asExpr() = c.getFormatExpr())
}
}
private predicate invalidFormatString(
InvalidFormatString src, DataFlow::PathNode source, DataFlow::PathNode sink, string msg,
FormatCall call, string callString
) {
source.getNode().asExpr() = src and
sink.getNode().asExpr() = call.getFormatExpr() and
any(FormatConfiguration conf).hasFlowPath(source, sink) and
call.hasInsertions() and
msg = "Invalid format string used in $@ formatting call." and
callString = "this"
}
private predicate unusedArgument(
FormatCall call, DataFlow::PathNode source, DataFlow::PathNode sink, string msg,
ValidFormatString src, string srcString, Expr unusedExpr, string unusedString
) {
exists(int unused |
source.getNode().asExpr() = src and
sink.getNode().asExpr() = call.getFormatExpr() and
any(FormatConfiguration conf).hasFlowPath(source, sink) and
unused = call.getASuppliedArgument() and
not unused = src.getAnInsert() and
not src.getValue() = "" and
msg = "The $@ ignores $@." and
srcString = "format string" and
unusedExpr = call.getSuppliedExpr(unused) and
unusedString = "this supplied value"
)
}
private predicate missingArgument(
FormatCall call, DataFlow::PathNode source, DataFlow::PathNode sink, string msg,
ValidFormatString src, string srcString
) {
exists(int used, int supplied |
source.getNode().asExpr() = src and
sink.getNode().asExpr() = call.getFormatExpr() and
any(FormatConfiguration conf).hasFlowPath(source, sink) and
used = src.getAnInsert() and
supplied = call.getSuppliedArguments() and
used >= supplied and
msg = "Argument '{" + used + "}' has not been supplied to $@ format string." and
srcString = "this"
)
}
from
Element alert, DataFlow::PathNode source, DataFlow::PathNode sink, string msg, Element extra1,
string extra1String, Element extra2, string extra2String
where
invalidFormatString(alert, source, sink, msg, extra1, extra1String) and
extra2 = extra1 and
extra2String = extra1String
or
unusedArgument(alert, source, sink, msg, extra1, extra1String, extra2, extra2String)
or
missingArgument(alert, source, sink, msg, extra1, extra1String) and
extra2 = extra1 and
extra2String = extra1String
select alert, source, sink, msg, extra1, extra1String, extra2, extra2String