introduce model for JSON.stringify and similar libraries

This commit is contained in:
Erik Krogh Kristensen
2020-08-04 14:35:09 +02:00
parent 68441bdf99
commit 5a3f67a682
8 changed files with 44 additions and 25 deletions

View File

@@ -67,7 +67,7 @@ predicate isBackslashEscape(StringReplaceCall mce, DataFlow::RegExpLiteralNode r
*/
predicate allBackslashesEscaped(DataFlow::Node nd) {
// `JSON.stringify` escapes backslashes
nd = DataFlow::globalVarRef("JSON").getAMemberCall("stringify")
nd instanceof JsonSerializeCall
or
// check whether `nd` itself escapes backslashes
exists(DataFlow::RegExpLiteralNode rel | isBackslashEscape(nd, rel) |

View File

@@ -34,6 +34,7 @@ import semmle.javascript.InclusionTests
import semmle.javascript.JSDoc
import semmle.javascript.JSON
import semmle.javascript.JsonParsers
import semmle.javascript.JsonSerializers
import semmle.javascript.JSX
import semmle.javascript.Lines
import semmle.javascript.Locations

View File

@@ -0,0 +1,34 @@
/**
* Provides classes for working with JSON serializers.
*/
import javascript
/**
* A call to a JSON serializer such as `JSON.stringify` or `require("util").inspect`.
*/
class JsonSerializeCall extends DataFlow::CallNode {
JsonSerializeCall() {
exists(DataFlow::SourceNode callee | this = callee.getACall() |
callee = DataFlow::globalVarRef("JSON").getAPropertyRead("stringify") or
callee = DataFlow::moduleMember("json3", "stringify") or
callee =
DataFlow::moduleImport(["json-stringify-safe", "json-stable-stringify", "stringify-object",
"fast-json-stable-stringify", "fast-safe-stringify", "javascript-stringify",
"js-stringify"]) or
// require("util").inspect() and similar
callee = DataFlow::moduleMember("util", "inspect") or
callee = DataFlow::moduleImport(["pretty-format", "object-inspect"])
)
}
/**
* Gets the data flow node holding the input object to be serialized.
*/
DataFlow::Node getInput() { result = getArgument(0) }
/**
* Gets the data flow node holding the resulting string.
*/
DataFlow::SourceNode getOutput() { result = this }
}

View File

@@ -543,8 +543,8 @@ module TaintTracking {
/**
* A taint propagating data flow edge arising from JSON unparsing.
*/
private class JsonStringifyTaintStep extends AdditionalTaintStep, DataFlow::MethodCallNode {
JsonStringifyTaintStep() { this = DataFlow::globalVarRef("JSON").getAMemberCall("stringify") }
private class JsonStringifyTaintStep extends AdditionalTaintStep, DataFlow::CallNode {
JsonStringifyTaintStep() { this instanceof JsonSerializeCall }
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = getArgument(0) and succ = this
@@ -693,18 +693,6 @@ module TaintTracking {
}
}
/**
* A taint step through the Node.JS function `util.inspect(..)`.
*/
class UtilInspectTaintStep extends AdditionalTaintStep, DataFlow::InvokeNode {
UtilInspectTaintStep() { this = DataFlow::moduleImport("util").getAMemberCall("inspect") }
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
succ = this and
this.getAnArgument() = pred
}
}
private module RegExpCaptureSteps {
/** Gets a reference to a string derived from the most recent RegExp match, such as `RegExp.$1`. */
private DataFlow::PropRead getAStaticCaptureRef() {

View File

@@ -28,9 +28,7 @@ private class RemoteFlowPassword extends HeuristicSource, RemoteFlowSource {
*/
private class JSONStringifyAsCommandInjectionSource extends HeuristicSource,
CommandInjection::Source {
JSONStringifyAsCommandInjectionSource() {
this = DataFlow::globalVarRef("JSON").getAMemberCall("stringify")
}
JSONStringifyAsCommandInjectionSource() { this instanceof JsonSerializeCall }
override string getSourceType() { result = "a string from JSON.stringify" }
}

View File

@@ -202,10 +202,9 @@ module CleartextLogging {
exists(DataFlow::PropWrite write, DataFlow::PropRead read |
read = write.getRhs()
or
exists(DataFlow::MethodCallNode stringify |
stringify = write.getRhs() and
stringify = DataFlow::globalVarRef("JSON").getAMethodCall("stringify") and
stringify.getArgument(0) = read
exists(JsonSerializeCall stringify |
stringify.getOutput() = write.getRhs() and
stringify.getInput() = read
)
|
not exists(write.getPropertyName()) and

View File

@@ -36,7 +36,7 @@ module ImproperCodeSanitization {
* A call to `JSON.stringify()` seen as a source for improper code sanitization
*/
class JSONStringifyAsSource extends Source {
JSONStringifyAsSource() { this = DataFlow::globalVarRef("JSON").getAMemberCall("stringify") }
JSONStringifyAsSource() { this instanceof JsonSerializeCall }
}
/**

View File

@@ -49,8 +49,7 @@ module PostMessageStar {
exists(DataFlow::InvokeNode toString | toString = trg |
toString.(DataFlow::MethodCallNode).calls(src, "toString")
or
toString = DataFlow::globalVarRef("JSON").getAMemberCall("stringify") and
src = toString.getArgument(0)
src = toString.(JsonSerializeCall).getInput()
) and
inlbl instanceof PartiallyTaintedObject and
outlbl.isTaint()